Skip to content

Commit

Permalink
feat: toExports
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Feb 25, 2022
1 parent 4964f9b commit b2baa8a
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 9 deletions.
25 changes: 17 additions & 8 deletions src/context.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { detectSyntax } from 'mlly'
import escapeRE from 'escape-string-regexp'
import type { Import, UnimportOptions } from './types'
import { excludeRE, stripCommentsAndStrings, separatorRE, importAsRE, toTypeDeclrationFile, addImportToCode, dedupeImports } from './utils'
import { excludeRE, stripCommentsAndStrings, separatorRE, importAsRE, toTypeDeclrationFile, addImportToCode, dedupeImports, toExports } from './utils'
import { resolveBuiltinPresets } from './preset'
import { normalizeImports } from '.'

interface Context {
readonly imports: Import[]
Expand All @@ -15,11 +16,17 @@ interface Context {
export type Unimport = ReturnType<typeof createUnimport>

export function createUnimport (opts: Partial<UnimportOptions>) {
// Cache for combine imports
let _combinedImports: Import[] | undefined

const ctx: Context = {
staticImports: [].concat(opts.imports).filter(Boolean),
dynamicImports: [],
get imports () {
return [...this.staticImports, ...this.dynamicImports]
if (!_combinedImports) {
_combinedImports = reload()
}
return _combinedImports
},
map: new Map(),
matchRE: /__never__/g
Expand All @@ -28,31 +35,32 @@ export function createUnimport (opts: Partial<UnimportOptions>) {
// Resolve presets
ctx.staticImports.push(...resolveBuiltinPresets(opts.presets || []))

// Normalize imports
for (const _import of ctx.imports) {
_import.as = _import.as || _import.name
}

// Detect duplicates
ctx.staticImports = dedupeImports(ctx.staticImports, console.warn)

function reload () {
const imports = dedupeImports(ctx.imports, console.warn)
// Combine static and dynamic imports
const imports = normalizeImports(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 imports) {
ctx.map.set(_import.as, _import)
}

return imports
}

function appendDynamicImports (imports: Import[]) {
ctx.dynamicImports.push(...imports)
_combinedImports = undefined
}

function clearDynamicImports () {
ctx.dynamicImports = []
_combinedImports = undefined
}

reload()
Expand All @@ -63,6 +71,7 @@ export function createUnimport (opts: Partial<UnimportOptions>) {
clearDynamicImports,
detectImports: (code: string) => detectImports(code, ctx),
injectImports: (code: string, mergeExisting?: boolean) => injectImports(code, ctx, mergeExisting),
toExports: () => toExports(ctx.imports),
generateTypeDecarations: () => toTypeDeclrationFile(ctx.imports)
}
}
Expand Down
22 changes: 21 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,20 @@ export function dedupeImports (imports: Import[], warn: (msg: string) => void) {
export function toExports (imports: Import[]) {
const map = toImportModuleMap(imports)
return Object.entries(map)
.map(([name, imports]) => `export { ${Array.from(imports).map(i => stringifyImportAlias(i, false)).join(', ')} } from '${name}';`)
.flatMap(([name, imports]) => {
const entries: string[] = []
const filtered = Array.from(imports).filter((i) => {
if (i.name === '*') {
entries.push(`export * as ${i.as} from '${name}';`)
return false
}
return true
})
if (filtered.length) {
entries.push(`export { ${filtered.map(i => stringifyImportAlias(i, false)).join(', ')} } from '${name}';`)
}
return entries
})
.join('\n')
}

Expand Down Expand Up @@ -175,3 +188,10 @@ export function addImportToCode (code: string, imports: Import[], isCJS = false,
code: s.toString()
}
}

export function normalizeImports (imports: Import[]): Import[] {
for (const _import of imports) {
_import.as = _import.as || _import.name
}
return imports
}
64 changes: 64 additions & 0 deletions test/to-export.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { describe, it, expect } from 'vitest'
import { Import } from '../src/types'
import { toExports } from '../src/utils'

describe('toExports', () => {
it('basic', () => {
const imports: Import[] = [{ from: 'test-id', name: 'fooBar', as: 'fooBar' }]
expect(toExports(imports))
.toMatchInlineSnapshot('"export { fooBar } from \'test-id\';"')
})

it('alias', () => {
const imports: Import[] = [{ from: 'test-id', name: 'foo', as: 'bar' }]
expect(toExports(imports))
.toMatchInlineSnapshot('"export { foo as bar } from \'test-id\';"')
})

it('multiple', () => {
const imports: Import[] = [
{ from: 'test1', name: 'foo', as: 'foo' },
{ from: 'test1', name: 'bar', as: 'bar' },
{ from: 'test2', name: 'foobar', as: 'foobar' }
]
expect(toExports(imports))
.toMatchInlineSnapshot(`
"export { foo, bar } from 'test1';
export { foobar } from 'test2';"
`)
})

it('default', () => {
const imports: Import[] = [
{ from: 'test1', name: 'default', as: 'foo' }
]
expect(toExports(imports))
.toMatchInlineSnapshot('"export { default as foo } from \'test1\';"')
})

it('import all as', () => {
const imports: Import[] = [
{ from: 'test1', name: '*', as: 'foo' }
]
expect(toExports(imports))
.toMatchInlineSnapshot('"export * as foo from \'test1\';"')
})

it('mixed', () => {
const imports: Import[] = [
{ from: 'test1', name: '*', as: 'foo' },
{ from: 'test1', name: '*', as: 'bar' },
{ from: 'test1', name: 'foo', as: 'foo' },
{ from: 'test1', name: 'bar', as: 'bar' },
{ from: 'test2', name: 'foobar', as: 'foobar' },
{ from: 'test2', name: 'default', as: 'defaultAlias' }
]
expect(toExports(imports))
.toMatchInlineSnapshot(`
"export * as foo from 'test1';
export * as bar from 'test1';
export { foo, bar } from 'test1';
export { foobar, default as defaultAlias } from 'test2';"
`)
})
})
File renamed without changes.

0 comments on commit b2baa8a

Please sign in to comment.