From 4964f9b52c8a3c5a10f266c3b9740b02d6427ada Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 25 Feb 2022 13:35:42 +0800 Subject: [PATCH] feat: dynamic imports --- src/context.ts | 44 ++++++++++++++++++++++++++++++++----------- src/unplugin.ts | 2 +- test/fixtures.test.ts | 6 +++--- test/index.test.ts | 8 ++++---- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/context.ts b/src/context.ts index dd5866c1..54bf9e8e 100644 --- a/src/context.ts +++ b/src/context.ts @@ -5,7 +5,9 @@ import { excludeRE, stripCommentsAndStrings, separatorRE, importAsRE, toTypeDecl import { resolveBuiltinPresets } from './preset' interface Context { - imports: Import[] + readonly imports: Import[] + staticImports: Import[] + dynamicImports: Import[] matchRE: RegExp map: Map } @@ -14,13 +16,17 @@ export type Unimport = ReturnType export function createUnimport (opts: Partial) { const ctx: Context = { - imports: [].concat(opts.imports).filter(Boolean), + staticImports: [].concat(opts.imports).filter(Boolean), + dynamicImports: [], + get imports () { + return [...this.staticImports, ...this.dynamicImports] + }, map: new Map(), matchRE: /__never__/g } // Resolve presets - ctx.imports.push(...resolveBuiltinPresets(opts.presets || [])) + ctx.staticImports.push(...resolveBuiltinPresets(opts.presets || [])) // Normalize imports for (const _import of ctx.imports) { @@ -28,19 +34,35 @@ export function createUnimport (opts: Partial) { } // Detect duplicates - ctx.imports = dedupeImports(ctx.imports, console.warn) + ctx.staticImports = dedupeImports(ctx.staticImports, console.warn) - // Create regex - ctx.matchRE = new RegExp(`(?:\\b|^)(${ctx.imports.map(i => escapeRE(i.as)).join('|')})(?:\\b|\\()`, 'g') + function reload () { + const imports = dedupeImports(ctx.imports, console.warn) + // Create regex + ctx.matchRE = new RegExp(`(?:\\b|^)(${imports.map(i => escapeRE(i.as)).join('|')})(?:\\b|\\()`, 'g') - // Create map - for (const _import of ctx.imports) { - ctx.map.set(_import.as, _import) + // Create map + for (const _import of imports) { + ctx.map.set(_import.as, _import) + } + } + + function appendDynamicImports (imports: Import[]) { + ctx.dynamicImports.push(...imports) } + function clearDynamicImports () { + ctx.dynamicImports = [] + } + + reload() + return { + reload, + appendDynamicImports, + clearDynamicImports, detectImports: (code: string) => detectImports(code, ctx), - addImports: (code: string, mergeExisting?: boolean) => addImports(code, ctx, mergeExisting), + injectImports: (code: string, mergeExisting?: boolean) => injectImports(code, ctx, mergeExisting), generateTypeDecarations: () => toTypeDeclrationFile(ctx.imports) } } @@ -77,7 +99,7 @@ function detectImports (code: string, ctx: Context) { } } -function addImports (code: string, ctx: Context, mergeExisting?: boolean) { +function injectImports (code: string, ctx: Context, mergeExisting?: boolean) { const { isCJSContext, matchedImports } = detectImports(code, ctx) return addImportToCode(code, matchedImports, isCJSContext, mergeExisting) diff --git a/src/unplugin.ts b/src/unplugin.ts index a57309a3..02962bda 100644 --- a/src/unplugin.ts +++ b/src/unplugin.ts @@ -27,7 +27,7 @@ export default createUnplugin>((options) => { return filter(id) }, transform (_code) { - const { code, s } = ctx.addImports(_code) + const { code, s } = ctx.injectImports(_code) if (code === _code) { return } diff --git a/test/fixtures.test.ts b/test/fixtures.test.ts index 59af4bb1..0f1200c8 100644 --- a/test/fixtures.test.ts +++ b/test/fixtures.test.ts @@ -7,7 +7,7 @@ import { createUnimport } from '../src/context' const UNMODIFIED_MARK = '@test-unmodified' describe('fixtures', async () => { - const { addImports } = createUnimport({ + const { injectImports } = createUnimport({ imports: [ { name: 'default', from: 'default', as: 'customDefault' }, { name: 'foobar', from: 'foobar', as: 'foobar' } @@ -41,13 +41,13 @@ describe('fixtures', async () => { for (const file of files) { it(file, async () => { const code = await fs.readFile(resolve(root, file), 'utf-8') - const pass1 = (await addImports(code))?.code ?? code + const pass1 = (await injectImports(code))?.code ?? code if (code.includes(UNMODIFIED_MARK)) { expect(pass1).toBe(code) } else { expect(pass1).toMatchSnapshot() expect(pass1).not.toBe(code) - const pass2 = (await addImports(pass1))?.code ?? pass1 + const pass2 = (await injectImports(pass1))?.code ?? pass1 expect(pass2).toBe(pass1) } }) diff --git a/test/index.test.ts b/test/index.test.ts index f08ff970..1935101a 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -1,12 +1,12 @@ import { expect, describe, test } from 'vitest' import { createUnimport } from '../src' -describe('unimport', () => { - test('Adds basic import', () => { - const { addImports } = createUnimport({ +describe('inject import', () => { + test('basic', () => { + const { injectImports } = createUnimport({ imports: [{ name: 'fooBar', from: 'test-id' }] }) - expect(addImports('console.log(fooBar())').code) + expect(injectImports('console.log(fooBar())').code) .toMatchInlineSnapshot('"import { fooBar } from \'test-id\';console.log(fooBar())"') }) })