|
1 | | -import { mkdir, writeFile } from 'node:fs/promises' |
| 1 | +import { mkdir, readFile, writeFile } from 'node:fs/promises' |
2 | 2 | import path from 'node:path' |
3 | 3 | import { createFilter } from '@rollup/pluginutils' |
4 | | -import { createUnplugin, type UnpluginInstance } from 'unplugin' |
| 4 | +import { parseAsync } from 'oxc-parser' |
| 5 | +import { |
| 6 | + createUnplugin, |
| 7 | + type UnpluginBuildContext, |
| 8 | + type UnpluginContext, |
| 9 | + type UnpluginInstance, |
| 10 | +} from 'unplugin' |
5 | 11 | import { resolveOptions, type Options } from './core/options' |
6 | 12 | import { |
7 | 13 | oxcTransform, |
8 | 14 | swcTransform, |
9 | 15 | tsTransform, |
10 | 16 | type TransformResult, |
11 | 17 | } from './core/transformer' |
12 | | -import type { Plugin } from 'rollup' |
| 18 | +import type { Plugin, PluginContext } from 'rollup' |
13 | 19 |
|
14 | 20 | export type { Options } |
15 | 21 |
|
16 | 22 | export const IsolatedDecl: UnpluginInstance<Options | undefined, false> = |
17 | | - createUnplugin((rawOptions = {}) => { |
| 23 | + createUnplugin((rawOptions = {}, meta) => { |
18 | 24 | const options = resolveOptions(rawOptions) |
19 | 25 | const filter = createFilter(options.include, options.exclude) |
20 | 26 |
|
@@ -61,32 +67,8 @@ export const IsolatedDecl: UnpluginInstance<Options | undefined, false> = |
61 | 67 | return filter(id) |
62 | 68 | }, |
63 | 69 |
|
64 | | - async transform(code, id): Promise<undefined> { |
65 | | - let result: TransformResult |
66 | | - switch (options.transformer) { |
67 | | - case 'oxc': |
68 | | - result = oxcTransform(id, code) |
69 | | - break |
70 | | - case 'swc': |
71 | | - result = await swcTransform(id, code) |
72 | | - break |
73 | | - case 'typescript': |
74 | | - result = await tsTransform( |
75 | | - id, |
76 | | - code, |
77 | | - (options as any).transformOptions, |
78 | | - ) |
79 | | - } |
80 | | - const { sourceText, errors } = result |
81 | | - if (errors.length) { |
82 | | - if (options.ignoreErrors) { |
83 | | - this.warn(errors[0]) |
84 | | - } else { |
85 | | - this.error(errors[0]) |
86 | | - return |
87 | | - } |
88 | | - } |
89 | | - addOutput(id, sourceText) |
| 70 | + transform(code, id): Promise<undefined> { |
| 71 | + return transform.call(this, code, id) |
90 | 72 | }, |
91 | 73 |
|
92 | 74 | esbuild: { |
@@ -155,6 +137,80 @@ export const IsolatedDecl: UnpluginInstance<Options | undefined, false> = |
155 | 137 | ...rollup, |
156 | 138 | }, |
157 | 139 | } |
| 140 | + |
| 141 | + async function transform( |
| 142 | + this: UnpluginBuildContext & UnpluginContext, |
| 143 | + code: string, |
| 144 | + id: string, |
| 145 | + ): Promise<undefined> { |
| 146 | + let result: TransformResult |
| 147 | + switch (options.transformer) { |
| 148 | + case 'oxc': |
| 149 | + result = oxcTransform(id, code) |
| 150 | + break |
| 151 | + case 'swc': |
| 152 | + result = await swcTransform(id, code) |
| 153 | + break |
| 154 | + case 'typescript': |
| 155 | + result = await tsTransform( |
| 156 | + id, |
| 157 | + code, |
| 158 | + (options as any).transformOptions, |
| 159 | + ) |
| 160 | + } |
| 161 | + const { sourceText, errors } = result |
| 162 | + if (errors.length) { |
| 163 | + if (options.ignoreErrors) { |
| 164 | + this.warn(errors[0]) |
| 165 | + } else { |
| 166 | + this.error(errors[0]) |
| 167 | + return |
| 168 | + } |
| 169 | + } |
| 170 | + addOutput(id, sourceText) |
| 171 | + |
| 172 | + let program: any |
| 173 | + try { |
| 174 | + program = JSON.parse( |
| 175 | + (await parseAsync(code, { sourceFilename: id })).program, |
| 176 | + ) |
| 177 | + } catch { |
| 178 | + return |
| 179 | + } |
| 180 | + const typeImports = program.body.filter((node: any) => { |
| 181 | + if (node.type !== 'ImportDeclaration') return false |
| 182 | + if (node.importKind === 'type') return true |
| 183 | + return (node.specifiers || []).every( |
| 184 | + (spec: any) => |
| 185 | + spec.type === 'ImportSpecifier' && spec.importKind === 'type', |
| 186 | + ) |
| 187 | + }) |
| 188 | + |
| 189 | + const resolve = async (id: string, importer: string) => { |
| 190 | + if (meta.framework === 'esbuild') { |
| 191 | + return ( |
| 192 | + await meta.build!.resolve(id, { |
| 193 | + importer, |
| 194 | + resolveDir: path.dirname(importer), |
| 195 | + kind: 'import-statement', |
| 196 | + }) |
| 197 | + ).path |
| 198 | + } |
| 199 | + return (await (this as any as PluginContext).resolve(id, importer))?.id |
| 200 | + } |
| 201 | + for (const i of typeImports) { |
| 202 | + const resolved = await resolve(i.source.value, id) |
| 203 | + if (resolved) { |
| 204 | + let source: string |
| 205 | + try { |
| 206 | + source = await readFile(resolved, 'utf8') |
| 207 | + } catch { |
| 208 | + continue |
| 209 | + } |
| 210 | + transform.call(this, source, resolved) |
| 211 | + } |
| 212 | + } |
| 213 | + } |
158 | 214 | }) |
159 | 215 |
|
160 | 216 | function lowestCommonAncestor(...filepaths: string[]) { |
|
0 commit comments