Skip to content

Commit 075e7f2

Browse files
committed
Split shared
1 parent c1e6008 commit 075e7f2

9 files changed

Lines changed: 177 additions & 43 deletions

File tree

packages/next-plugin/src/css-loader.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { existsSync, readFileSync } from 'node:fs'
22
import { Agent, request } from 'node:http'
33

4+
import { getFileNumByFilename } from '@devup-ui/plugin-utils'
45
import {
56
getCss,
67
importClassMap,
@@ -10,15 +11,6 @@ import {
1011
} from '@devup-ui/wasm'
1112
import type { RawLoaderDefinitionFunction } from 'webpack'
1213

13-
function getFileNumByFilename(filename: string) {
14-
// Handle query parameter format: devup-ui.css?fileNum=79
15-
// Turbopack may embed query params in resourcePath
16-
const queryMatch = filename.match(/[?&]fileNum=(\d+)/)
17-
if (queryMatch) return parseInt(queryMatch[1])
18-
if (filename.endsWith('devup-ui.css')) return null
19-
return parseInt(filename.split('devup-ui-')[1].split('.')[0])
20-
}
21-
2214
export interface DevupUICssLoaderOptions {
2315
// turbo
2416
watch: boolean

packages/next-plugin/src/plugin.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { existsSync, mkdirSync, writeFileSync } from 'node:fs'
22
import { join, relative, resolve } from 'node:path'
33

4-
import { loadDevupConfigSync, mergeImportAliases } from '@devup-ui/plugin-utils'
4+
import {
5+
createNodeModulesExcludeRegex,
6+
loadDevupConfigSync,
7+
mergeImportAliases,
8+
} from '@devup-ui/plugin-utils'
59
import {
610
exportClassMap,
711
exportFileMap,
@@ -86,11 +90,7 @@ export function DevupUI(
8690
writeFileSync(join(distDir, 'theme.d.ts'), themeInterface)
8791
}
8892
// disable turbo parallel
89-
const excludeRegex = new RegExp(
90-
`(node_modules(?!.*(${['@devup-ui', ...include]
91-
.join('|')
92-
.replaceAll('/', '[\\/\\\\_]')})([\\/\\\\.]|$)))|(.mdx.[tj]sx?$)`,
93-
)
93+
const excludeRegex = createNodeModulesExcludeRegex(include, '.mdx.[tj]sx?$')
9494

9595
const coordinatorPortFile = join(distDir, 'coordinator.port')
9696

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { describe, expect, it } from 'bun:test'
2+
3+
import {
4+
createNodeModulesExcludeRegex,
5+
type DevupUIBasePluginOptions,
6+
getFileNumByFilename,
7+
} from '../shared'
8+
9+
describe('getFileNumByFilename', () => {
10+
it('should return null for devup-ui.css', () => {
11+
expect(getFileNumByFilename('devup-ui.css')).toBeNull()
12+
})
13+
14+
it('should return file number for devup-ui-5.css', () => {
15+
expect(getFileNumByFilename('devup-ui-5.css')).toBe(5)
16+
})
17+
18+
it('should return file number for devup-ui-123.css', () => {
19+
expect(getFileNumByFilename('devup-ui-123.css')).toBe(123)
20+
})
21+
22+
it('should handle query params: devup-ui.css?fileNum=79', () => {
23+
expect(getFileNumByFilename('devup-ui.css?fileNum=79')).toBe(79)
24+
})
25+
26+
it('should handle path with query: /path/to/devup-ui.css?fileNum=42', () => {
27+
expect(getFileNumByFilename('/path/to/devup-ui.css?fileNum=42')).toBe(42)
28+
})
29+
30+
it('should return null for path/to/devup-ui.css (no number, no query)', () => {
31+
expect(getFileNumByFilename('path/to/devup-ui.css')).toBeNull()
32+
})
33+
})
34+
35+
describe('createNodeModulesExcludeRegex', () => {
36+
it('should match node_modules paths that should be excluded', () => {
37+
const regex = createNodeModulesExcludeRegex([])
38+
expect(regex.test('node_modules/some-package/index.js')).toBe(true)
39+
expect(regex.test('/path/to/node_modules/lodash/index.js')).toBe(true)
40+
})
41+
42+
it('should NOT match @devup-ui paths', () => {
43+
const regex = createNodeModulesExcludeRegex([])
44+
expect(regex.test('node_modules/@devup-ui/react/index.js')).toBe(false)
45+
expect(regex.test('node_modules/@devup-ui/components/index.js')).toBe(false)
46+
})
47+
48+
it('should NOT match included packages', () => {
49+
const regex = createNodeModulesExcludeRegex(['my-company/design-system'])
50+
expect(regex.test('node_modules/my-company/design-system/index.js')).toBe(
51+
false,
52+
)
53+
})
54+
55+
it('should handle extra excludes parameter', () => {
56+
const regex = createNodeModulesExcludeRegex([], '.mdx.[tj]sx?$')
57+
// Should match node_modules
58+
expect(regex.test('node_modules/some-package/index.js')).toBe(true)
59+
// Should also match .mdx.tsx files
60+
expect(regex.test('src/page.mdx.tsx')).toBe(true)
61+
expect(regex.test('src/page.mdx.jsx')).toBe(true)
62+
expect(regex.test('src/page.mdx.ts')).toBe(true)
63+
// Should NOT match @devup-ui
64+
expect(regex.test('node_modules/@devup-ui/react/index.js')).toBe(false)
65+
})
66+
67+
it('should handle empty include array', () => {
68+
const regex = createNodeModulesExcludeRegex([])
69+
expect(regex.test('node_modules/some-package/index.js')).toBe(true)
70+
expect(regex.test('node_modules/@devup-ui/react/index.js')).toBe(false)
71+
})
72+
})
73+
74+
describe('DevupUIBasePluginOptions', () => {
75+
it('should be usable as a type', () => {
76+
const options: DevupUIBasePluginOptions = {
77+
package: '@devup-ui/react',
78+
cssDir: 'df/devup-ui',
79+
devupFile: 'devup.json',
80+
distDir: 'df',
81+
debug: false,
82+
include: [],
83+
singleCss: false,
84+
}
85+
expect(options.package).toBe('@devup-ui/react')
86+
})
87+
88+
it('should accept optional fields', () => {
89+
const options: DevupUIBasePluginOptions = {
90+
package: '@devup-ui/react',
91+
cssDir: 'df/devup-ui',
92+
devupFile: 'devup.json',
93+
distDir: 'df',
94+
debug: false,
95+
include: [],
96+
singleCss: false,
97+
prefix: 'my-prefix',
98+
importAliases: { '@emotion/styled': 'styled' },
99+
}
100+
expect(options.prefix).toBe('my-prefix')
101+
expect(options.importAliases).toEqual({ '@emotion/styled': 'styled' })
102+
})
103+
})

packages/plugin-utils/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
export { deepMerge, loadDevupConfig, loadDevupConfigSync } from './load-config'
2+
export {
3+
createNodeModulesExcludeRegex,
4+
type DevupUIBasePluginOptions,
5+
getFileNumByFilename,
6+
} from './shared'
27
export type {
38
DevupConfig,
49
DevupTheme,
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import type { ImportAliases } from './types'
2+
3+
/**
4+
* Extract file number from a devup-ui CSS filename.
5+
*
6+
* Handles both standard filenames (devup-ui-5.css) and query parameter
7+
* format (devup-ui.css?fileNum=79) used by Turbopack.
8+
*
9+
* @param filename - CSS filename or path to parse
10+
* @returns The file number, or null for the base devup-ui.css file
11+
*/
12+
export function getFileNumByFilename(filename: string): number | null {
13+
// Handle query parameter format: devup-ui.css?fileNum=79
14+
// Turbopack may embed query params in resourcePath
15+
const queryMatch = filename.match(/[?&]fileNum=(\d+)/)
16+
if (queryMatch) return parseInt(queryMatch[1])
17+
if (filename.endsWith('devup-ui.css')) return null
18+
return parseInt(filename.split('devup-ui-')[1].split('.')[0])
19+
}
20+
21+
/**
22+
* Create a regex that excludes node_modules paths, except for @devup-ui
23+
* and any additional included packages.
24+
*
25+
* @param include - Additional package names to include (not exclude)
26+
* @param extraExcludes - Optional extra regex pattern to OR with the node_modules exclusion
27+
* @returns A RegExp for use in bundler exclude/condition rules
28+
*/
29+
export function createNodeModulesExcludeRegex(
30+
include: string[],
31+
extraExcludes?: string,
32+
): RegExp {
33+
const base = `node_modules(?!.*(${['@devup-ui', ...include]
34+
.join('|')
35+
.replaceAll('/', '[\\/\\\\_]')})([\\/\\\\.]|$))`
36+
return new RegExp(extraExcludes ? `(${base})|(${extraExcludes})` : base)
37+
}
38+
39+
/**
40+
* Common plugin options shared across all devup-ui build plugins.
41+
*/
42+
export interface DevupUIBasePluginOptions {
43+
package: string
44+
cssDir: string
45+
devupFile: string
46+
distDir: string
47+
debug: boolean
48+
include: string[]
49+
singleCss: boolean
50+
prefix?: string
51+
importAliases?: ImportAliases
52+
}

packages/rsbuild-plugin/src/plugin.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { mkdir, writeFile } from 'node:fs/promises'
33
import { basename, join, resolve } from 'node:path'
44

55
import {
6+
createNodeModulesExcludeRegex,
67
type ImportAliases,
78
loadDevupConfig,
89
mergeImportAliases,
@@ -136,13 +137,7 @@ export const DevupUI = ({
136137
test: /\.(tsx|ts|js|mjs|jsx)$/,
137138
},
138139
async ({ code, resourcePath }) => {
139-
if (
140-
new RegExp(
141-
`node_modules(?!.*(${['@devup-ui', ...include]
142-
.join('|')
143-
.replaceAll('/', '[\\/\\\\_]')})([\\/\\\\.]|$))`,
144-
).test(resourcePath)
145-
)
140+
if (createNodeModulesExcludeRegex(include).test(resourcePath))
146141
return code
147142
const {
148143
code: retCode,

packages/vite-plugin/src/plugin.ts

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { mkdir, writeFile } from 'node:fs/promises'
33
import { basename, dirname, join, relative, resolve } from 'node:path'
44

55
import {
6+
createNodeModulesExcludeRegex,
7+
getFileNumByFilename,
68
type ImportAliases,
79
loadDevupConfig,
810
mergeImportAliases,
@@ -36,11 +38,6 @@ export interface DevupUIPluginOptions {
3638
importAliases?: ImportAliases
3739
}
3840

39-
function getFileNumByFilename(filename: string) {
40-
if (filename.endsWith('devup-ui.css')) return null
41-
return parseInt(filename.split('devup-ui-')[1].split('.')[0])
42-
}
43-
4441
async function writeDataFiles(
4542
options: Omit<DevupUIPluginOptions, 'extractCss' | 'debug' | 'include'>,
4643
) {
@@ -194,13 +191,7 @@ export function DevupUI({
194191

195192
const fileName = id.split('?')[0]
196193
if (!/\.(tsx|ts|js|mjs|jsx)$/i.test(fileName)) return
197-
if (
198-
new RegExp(
199-
`node_modules(?!.*(${['@devup-ui', ...include]
200-
.join('|')
201-
.replaceAll('/', '[\\/\\\\_]')})([\\/\\\\.]|$))`,
202-
).test(fileName)
203-
) {
194+
if (createNodeModulesExcludeRegex(include).test(fileName)) {
204195
return
205196
}
206197

packages/webpack-plugin/src/css-loader.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1+
import { getFileNumByFilename } from '@devup-ui/plugin-utils'
12
import { getCss } from '@devup-ui/wasm'
23
import type { RawLoaderDefinitionFunction } from 'webpack'
34

4-
function getFileNumByFilename(filename: string) {
5-
if (filename.endsWith('devup-ui.css')) return null
6-
return parseInt(filename.split('devup-ui-')[1].split('.')[0])
7-
}
8-
95
const devupUICssLoader: RawLoaderDefinitionFunction = function (_, map, meta) {
106
const fileNum = getFileNumByFilename(this.resourcePath)
117
this.callback(null, getCss(fileNum, true), map, meta)

packages/webpack-plugin/src/plugin.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { createRequire } from 'node:module'
44
import { join, resolve } from 'node:path'
55

66
import {
7+
createNodeModulesExcludeRegex,
78
type ImportAliases,
89
loadDevupConfigSync,
910
mergeImportAliases,
@@ -178,10 +179,9 @@ export class DevupUIWebpackPlugin {
178179
compiler.options.module.rules.push(
179180
{
180181
test: /\.(tsx|ts|js|mjs|jsx)$/,
181-
exclude: new RegExp(
182-
`(node_modules(?!.*(${['@devup-ui', ...this.options.include]
183-
.join('|')
184-
.replaceAll('/', '[\\/\\\\_]')})([\\/\\\\.]|$)))|(.mdx.[tj]sx?$)`,
182+
exclude: createNodeModulesExcludeRegex(
183+
this.options.include,
184+
'.mdx.[tj]sx?$',
185185
),
186186
enforce: 'pre',
187187
use: [

0 commit comments

Comments
 (0)