-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: auto global imports (nuxt#410)
Co-authored-by: Pooya Parsa <[email protected]>
- Loading branch information
Showing
25 changed files
with
274 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { defineBuildConfig } from 'unbuild' | ||
|
||
export default defineBuildConfig({ | ||
declaration: true, | ||
entries: [ | ||
'src/module' | ||
] | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = require('./dist/module') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{ | ||
"name": "@nuxt/global-imports", | ||
"version": "0.1.0", | ||
"repository": "nuxt/framework", | ||
"license": "MIT", | ||
"exports": { | ||
"./module": { | ||
"import": "./dist/module.mjs", | ||
"require": "./dist/module.js" | ||
} | ||
}, | ||
"types": "./dist/module.d.ts", | ||
"files": [ | ||
"dist" | ||
], | ||
"scripts": { | ||
"prepack": "unbuild" | ||
}, | ||
"dependencies": { | ||
"@nuxt/kit": "^0.6.4", | ||
"ufo": "^0.7.7", | ||
"unplugin": "^0.0.5", | ||
"upath": "^2.0.1" | ||
}, | ||
"devDependencies": { | ||
"unbuild": "^0.4.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
const VueAPIs = [ | ||
// lifecycle | ||
'onActivated', | ||
'onBeforeMount', | ||
'onBeforeUnmount', | ||
'onBeforeUpdate', | ||
'onDeactivated', | ||
'onErrorCaptured', | ||
'onMounted', | ||
'onServerPrefetch', | ||
'onUnmounted', | ||
'onUpdated', | ||
|
||
// reactivity, | ||
'computed', | ||
'customRef', | ||
'isReadonly', | ||
'isRef', | ||
'markRaw', | ||
'reactive', | ||
'readonly', | ||
'ref', | ||
'shallowReactive', | ||
'shallowReadonly', | ||
'shallowRef', | ||
'toRaw', | ||
'toRef', | ||
'toRefs', | ||
'triggerRef', | ||
'unref', | ||
'watch', | ||
'watchEffect', | ||
|
||
// component | ||
'defineComponent', | ||
'defineAsyncComponent', | ||
'getCurrentInstance', | ||
'h', | ||
'inject', | ||
'nextTick', | ||
'provide', | ||
'useCssModule' | ||
] | ||
|
||
const nuxtComposition = [ | ||
'useAsyncData', | ||
'asyncData', | ||
'defineNuxtComponent', | ||
'useNuxt', | ||
'defineNuxtPlugin', | ||
'useMeta' | ||
] | ||
|
||
export const defaultIdentifiers = Object.fromEntries([ | ||
...VueAPIs.map(name => [name, 'vue']), | ||
...nuxtComposition.map(name => [name, '@nuxt/app']) | ||
]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { addVitePlugin, addWebpackPlugin, defineNuxtModule, addTemplate, addPluginTemplate } from '@nuxt/kit' | ||
import { resolve } from 'upath' | ||
import type { Identifiers, GlobalImportsOptions } from './types' | ||
import { TrsnsformPlugin } from './transform' | ||
import { defaultIdentifiers } from './identifiers' | ||
|
||
export default defineNuxtModule<GlobalImportsOptions>({ | ||
name: 'global-imports', | ||
configKey: 'globalImports', | ||
defaults: { identifiers: defaultIdentifiers }, | ||
setup ({ identifiers }, nuxt) { | ||
if (nuxt.options.dev) { | ||
// Add all imports to globalThis in development mode | ||
addPluginTemplate({ | ||
filename: 'global-imports.mjs', | ||
src: '', | ||
getContents: () => { | ||
const imports = toImports(Object.entries(identifiers)) | ||
const globalThisSet = Object.keys(identifiers).map(name => `globalThis.${name} = ${name};`).join('\n') | ||
return `${imports}\n\n${globalThisSet}\n\nexport default () => {};` | ||
} | ||
}) | ||
} else { | ||
// Transform to inject imports in production mode | ||
addVitePlugin(TrsnsformPlugin.vite(identifiers)) | ||
addWebpackPlugin(TrsnsformPlugin.webpack(identifiers)) | ||
} | ||
|
||
// Add types | ||
addTemplate({ | ||
filename: 'global-imports.d.ts', | ||
write: true, | ||
getContents: () => `// Generated by global imports | ||
declare global { | ||
${Object.entries(identifiers).map(([api, moduleName]) => ` const ${api}: typeof import('${moduleName}')['${api}']`).join('\n')} | ||
}\nexport {}` | ||
}) | ||
nuxt.hook('prepare:types', ({ references }) => { | ||
references.push({ path: resolve(nuxt.options.buildDir, 'global-imports.d.ts') }) | ||
}) | ||
} | ||
}) | ||
|
||
function toImports (identifiers: Identifiers) { | ||
const map: Record<string, Set<string>> = {} | ||
|
||
identifiers.forEach(([name, moduleName]) => { | ||
if (!map[moduleName]) { | ||
map[moduleName] = new Set() | ||
} | ||
map[moduleName].add(name) | ||
}) | ||
|
||
return Object.entries(map) | ||
.map(([name, imports]) => `import { ${Array.from(imports).join(', ')} } from '${name}';`) | ||
.join('\n') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { createUnplugin } from 'unplugin' | ||
import { parseQuery, parseURL } from 'ufo' | ||
import { IdentifierMap } from './types' | ||
|
||
const excludeRegex = [ | ||
// imported from other module | ||
/\bimport\s*\{([\s\S]*?)\}\s*from\b/g, | ||
// defined as function | ||
/\bfunction\s*([\s\S]+?)\s*\(/g, | ||
// defined as local variable | ||
/\b(?:const|let|var)\s*([\w\d_$]+?)\b/g | ||
] | ||
|
||
export const TrsnsformPlugin = createUnplugin((map: IdentifierMap) => { | ||
const regex = new RegExp('\\b(' + (Object.keys(map).join('|')) + ')\\b', 'g') | ||
|
||
return { | ||
name: 'nuxt-global-imports-transform', | ||
enforce: 'post', | ||
transformInclude (id) { | ||
const { pathname, search } = parseURL(id) | ||
const query = parseQuery(search) | ||
|
||
if (id.includes('node_modules')) { | ||
return false | ||
} | ||
|
||
// vue files | ||
if (pathname.endsWith('.vue') && (query.type === 'template' || !search)) { | ||
return true | ||
} | ||
|
||
// js files | ||
if (pathname.match(/\.((c|m)?j|t)sx?/g)) { | ||
return true | ||
} | ||
}, | ||
transform (code) { | ||
// find all possible injection | ||
const matched = new Set(Array.from(code.matchAll(regex)).map(i => i[1])) | ||
|
||
// remove those already defined | ||
for (const regex of excludeRegex) { | ||
Array.from(code.matchAll(regex)) | ||
.flatMap(i => i[1]?.split(',') || []) | ||
.forEach(i => matched.delete(i.trim())) | ||
} | ||
|
||
const modules: Record<string, string[]> = {} | ||
|
||
// group by module name | ||
Array.from(matched).forEach((name) => { | ||
const moduleName = map[name]! | ||
if (!modules[moduleName]) { | ||
modules[moduleName] = [] | ||
} | ||
modules[moduleName].push(name) | ||
}) | ||
|
||
// stringify import | ||
const imports = Object.entries(modules) | ||
.map(([moduleName, names]) => `import { ${names.join(',')} } from '${moduleName}';`) | ||
.join('') | ||
|
||
return imports + code | ||
} | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export type IdentifierMap = Record<string, string> | ||
export type Identifiers = [string, string][] | ||
|
||
export interface GlobalImportsOptions { | ||
identifiers?: IdentifierMap | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { expect } from 'chai' | ||
import { TrsnsformPlugin } from '../src/transform' | ||
|
||
describe('module:global-imports:build', () => { | ||
const { transform } = TrsnsformPlugin.raw({ ref: 'vue' }) | ||
|
||
it('should correct inject', () => { | ||
expect(transform('const a = ref(0)', '')) | ||
.to.equal('import { ref } from \'vue\';const a = ref(0)') | ||
}) | ||
|
||
it('should ignore imported', () => { | ||
expect(transform('import { ref } from "foo";const a = ref(0)', '')) | ||
.to.equal('import { ref } from "foo";const a = ref(0)') | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.