Skip to content

Commit

Permalink
feat: dynamic imports
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Feb 25, 2022
1 parent 30f16c3 commit 4964f9b
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 19 deletions.
44 changes: 33 additions & 11 deletions src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, Import>
}
Expand All @@ -14,33 +16,53 @@ export type Unimport = ReturnType<typeof createUnimport>

export function createUnimport (opts: Partial<UnimportOptions>) {
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) {
_import.as = _import.as || _import.name
}

// 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)
}
}
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/unplugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default createUnplugin<Partial<UnimportPluginOptions>>((options) => {
return filter(id)
},
transform (_code) {
const { code, s } = ctx.addImports(_code)
const { code, s } = ctx.injectImports(_code)
if (code === _code) {
return
}
Expand Down
6 changes: 3 additions & 3 deletions test/fixtures.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' }
Expand Down Expand Up @@ -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)
}
})
Expand Down
8 changes: 4 additions & 4 deletions test/index.test.ts
Original file line number Diff line number Diff line change
@@ -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())"')
})
})

0 comments on commit 4964f9b

Please sign in to comment.