Skip to content

Commit 99fbe5a

Browse files
committed
refactor: re-organize
1 parent 22d49e5 commit 99fbe5a

File tree

3 files changed

+88
-81
lines changed

3 files changed

+88
-81
lines changed

src/core/utils.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import path from 'node:path'
2+
3+
export function lowestCommonAncestor(...filepaths: string[]): string {
4+
if (filepaths.length === 0) return ''
5+
if (filepaths.length === 1) return path.dirname(filepaths[0])
6+
filepaths = filepaths.map((p) => p.replaceAll('\\', '/'))
7+
const [first, ...rest] = filepaths
8+
let ancestor = first.split('/')
9+
for (const filepath of rest) {
10+
const directories = filepath.split('/', ancestor.length)
11+
let index = 0
12+
for (const directory of directories) {
13+
if (directory === ancestor[index]) {
14+
index += 1
15+
} else {
16+
ancestor = ancestor.slice(0, index)
17+
break
18+
}
19+
}
20+
ancestor = ancestor.slice(0, index)
21+
}
22+
23+
return ancestor.length <= 1 && ancestor[0] === ''
24+
? `/${ancestor[0]}`
25+
: ancestor.join('/')
26+
}
27+
28+
export function stripExt(filename: string): string {
29+
return filename.replace(/\.(.?)[jt]sx?$/, '')
30+
}

src/index.ts

Lines changed: 57 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,15 @@ import {
1616
tsTransform,
1717
type TransformResult,
1818
} from './core/transformer'
19+
import { lowestCommonAncestor, stripExt } from './core/utils'
1920
import type * as OxcTypes from '@oxc-project/types'
2021
import type { PluginBuild } from 'esbuild'
21-
import type { Plugin, PluginContext } from 'rollup'
22+
import type {
23+
NormalizedInputOptions,
24+
NormalizedOutputOptions,
25+
Plugin,
26+
PluginContext,
27+
} from 'rollup'
2228

2329
export type { Options }
2430

@@ -28,70 +34,23 @@ export const IsolatedDecl: UnpluginInstance<Options | undefined, false> =
2834
const filter = createFilter(options.include, options.exclude)
2935

3036
const outputFiles: Record<string, string> = {}
31-
3237
function addOutput(filename: string, source: string) {
3338
outputFiles[stripExt(filename)] = source
3439
}
3540

3641
const rollup: Partial<Plugin> = {
37-
renderStart(outputOptions, inputOptions) {
38-
const { input } = inputOptions
39-
const inputMap = !Array.isArray(input)
40-
? Object.fromEntries(
41-
Object.entries(input).map(([k, v]) => [
42-
path.resolve(stripExt(v)),
43-
k,
44-
]),
45-
)
46-
: undefined
47-
const normalizedInput = Array.isArray(input)
48-
? input
49-
: Object.values(input)
50-
const outBase = lowestCommonAncestor(...normalizedInput)
51-
52-
if (typeof outputOptions.entryFileNames !== 'string') {
53-
return this.error('entryFileNames must be a string')
54-
}
55-
56-
let entryFileNames = outputOptions.entryFileNames.replace(
57-
/\.(.)?[jt]sx?$/,
58-
(_, s) => `.d.${s || ''}ts`,
59-
)
60-
61-
if (options.extraOutdir) {
62-
entryFileNames = path.join(options.extraOutdir, entryFileNames)
63-
}
64-
65-
for (let [outname, source] of Object.entries(outputFiles)) {
66-
const name: string =
67-
inputMap?.[outname] || path.relative(outBase, outname)
68-
const fileName = entryFileNames.replace('[name]', name)
69-
if (options.patchCjsDefaultExport && fileName.endsWith('.d.cts')) {
70-
source = patchCjsDefaultExport(source)
71-
}
72-
this.emitFile({
73-
type: 'asset',
74-
fileName,
75-
source,
76-
})
77-
}
78-
},
42+
renderStart: rollupRenderStart,
7943
}
8044

8145
return {
8246
name: 'unplugin-isolated-decl',
8347

84-
transformInclude(id) {
85-
return filter(id)
86-
},
87-
48+
transformInclude: (id) => filter(id),
8849
transform(code, id): Promise<undefined> {
8950
return transform(this, code, id)
9051
},
9152

92-
esbuild: {
93-
setup: esbuildSetup,
94-
},
53+
esbuild: { setup: esbuildSetup },
9554
rollup,
9655
rolldown: rollup as any,
9756
vite: {
@@ -214,6 +173,53 @@ export const IsolatedDecl: UnpluginInstance<Options | undefined, false> =
214173
}
215174
}
216175

176+
function rollupRenderStart(
177+
this: PluginContext,
178+
outputOptions: NormalizedOutputOptions,
179+
inputOptions: NormalizedInputOptions,
180+
) {
181+
const { input } = inputOptions
182+
const inputMap = !Array.isArray(input)
183+
? Object.fromEntries(
184+
Object.entries(input).map(([k, v]) => [
185+
path.resolve(stripExt(v)),
186+
k,
187+
]),
188+
)
189+
: undefined
190+
const normalizedInput = Array.isArray(input)
191+
? input
192+
: Object.values(input)
193+
const outBase = lowestCommonAncestor(...normalizedInput)
194+
195+
if (typeof outputOptions.entryFileNames !== 'string') {
196+
return this.error('entryFileNames must be a string')
197+
}
198+
199+
let entryFileNames = outputOptions.entryFileNames.replace(
200+
/\.(.)?[jt]sx?$/,
201+
(_, s) => `.d.${s || ''}ts`,
202+
)
203+
204+
if (options.extraOutdir) {
205+
entryFileNames = path.join(options.extraOutdir, entryFileNames)
206+
}
207+
208+
for (let [outname, source] of Object.entries(outputFiles)) {
209+
const name: string =
210+
inputMap?.[outname] || path.relative(outBase, outname)
211+
const fileName = entryFileNames.replace('[name]', name)
212+
if (options.patchCjsDefaultExport && fileName.endsWith('.d.cts')) {
213+
source = patchCjsDefaultExport(source)
214+
}
215+
this.emitFile({
216+
type: 'asset',
217+
fileName,
218+
source,
219+
})
220+
}
221+
}
222+
217223
function esbuildSetup(build: PluginBuild) {
218224
build.onEnd(async (result) => {
219225
const esbuildOptions = build.initialOptions
@@ -297,38 +303,9 @@ async function resolve(
297303
return { id: resolved.id, external: !!resolved.external }
298304
}
299305

300-
function stripExt(filename: string) {
301-
return filename.replace(/\.(.?)[jt]sx?$/, '')
302-
}
303-
304306
function patchCjsDefaultExport(source: string) {
305307
return source.replace(
306308
/(?<=(?:[;}]|^)\s*export\s*)(?:\{\s*([\w$]+)\s*as\s+default\s*\}|default\s+([\w$]+))/,
307309
(_, s1, s2) => `= ${s1 || s2}`,
308310
)
309311
}
310-
311-
export function lowestCommonAncestor(...filepaths: string[]): string {
312-
if (filepaths.length === 0) return ''
313-
if (filepaths.length === 1) return path.dirname(filepaths[0])
314-
filepaths = filepaths.map((p) => p.replaceAll('\\', '/'))
315-
const [first, ...rest] = filepaths
316-
let ancestor = first.split('/')
317-
for (const filepath of rest) {
318-
const directories = filepath.split('/', ancestor.length)
319-
let index = 0
320-
for (const directory of directories) {
321-
if (directory === ancestor[index]) {
322-
index += 1
323-
} else {
324-
ancestor = ancestor.slice(0, index)
325-
break
326-
}
327-
}
328-
ancestor = ancestor.slice(0, index)
329-
}
330-
331-
return ancestor.length <= 1 && ancestor[0] === ''
332-
? `/${ancestor[0]}`
333-
: ancestor.join('/')
334-
}

tests/paths.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect, test } from 'vitest'
2-
import { lowestCommonAncestor } from '../src'
2+
import { lowestCommonAncestor } from '../src/core/utils'
33

44
test('lowestCommonAncestor', () => {
55
expect(lowestCommonAncestor('/a/b', '/a')).toBe('/a')

0 commit comments

Comments
 (0)