Skip to content

Commit 423cb7b

Browse files
committed
feat: generate IntelliJ ide helper file
1 parent 9dc7333 commit 423cb7b

File tree

8 files changed

+118
-2
lines changed

8 files changed

+118
-2
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
"local-pkg": "^0.4.0",
8282
"magic-string": "^0.25.7",
8383
"minimatch": "^3.0.4",
84+
"pnpm": "^6.23.6",
8485
"resolve": "^1.20.0",
8586
"unplugin": "^0.2.21"
8687
},

pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/core/context.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ import { pascalCase, getNameFromFilePath, resolveAlias, matchGlobs, parseId } fr
88
import { resolveOptions } from './options'
99
import { searchComponents } from './fs/glob'
1010
import { generateDeclaration } from './declaration'
11+
import { generateIdeHelper } from './ideHelper'
1112
import transformer from './transformer'
1213

1314
const debug = {
1415
components: Debug('unplugin-vue-components:context:components'),
1516
search: Debug('unplugin-vue-components:context:search'),
1617
hmr: Debug('unplugin-vue-components:context:hmr'),
1718
decleration: Debug('unplugin-vue-components:decleration'),
19+
ideHelper: Debug('unplugin-vue-components:ideHelper'),
1820
env: Debug('unplugin-vue-components:env'),
1921
}
2022

@@ -37,6 +39,7 @@ export class Context {
3739
) {
3840
this.options = resolveOptions(rawOptions, this.root)
3941
this.generateDeclaration = throttle(500, false, this.generateDeclaration.bind(this))
42+
this.generateIdeHelper = throttle(500, false, this.generateIdeHelper.bind(this))
4043
this.setTransformer(this.options.transformer)
4144
}
4245

@@ -130,6 +133,7 @@ export class Context {
130133

131134
onUpdate(path: string) {
132135
this.generateDeclaration()
136+
this.generateIdeHelper()
133137

134138
if (!this._server)
135139
return
@@ -251,6 +255,14 @@ export class Context {
251255
generateDeclaration(this, this.options.root, this.options.dts)
252256
}
253257

258+
generateIdeHelper() {
259+
if (!this.options.generateIdeHelper)
260+
return
261+
262+
debug.ideHelper('generating')
263+
generateIdeHelper(this, this.options.root, this.options.generateIdeHelper)
264+
}
265+
254266
get componentNameMap() {
255267
return this._componentNameMap
256268
}

src/core/ideHelper.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { dirname, relative, isAbsolute } from 'path'
2+
import { promises as fs, existsSync } from 'fs'
3+
import { notNullish, slash } from '@antfu/utils'
4+
import { Context } from './context'
5+
import { getVueVersion } from './options'
6+
import { getTransformedPath } from './utils'
7+
8+
export async function generateIdeHelper(ctx: Context, root: string, filepath: string) {
9+
const imports: Record<string, string> = Object.fromEntries(
10+
Object.values({
11+
...ctx.componentNameMap,
12+
...ctx.componentCustomMap,
13+
})
14+
.map(({ path, name, importName }) => {
15+
if (!name)
16+
return undefined
17+
path = getTransformedPath(path, ctx)
18+
const related = isAbsolute(path)
19+
? `./${relative(dirname(filepath), path)}`
20+
: path
21+
22+
let entry = 'import '
23+
if (importName)
24+
entry += `{ ${importName} as ${name} }`
25+
else
26+
entry += name
27+
entry += ` from '${slash(related)}';`
28+
return [name, entry]
29+
})
30+
.filter(notNullish),
31+
)
32+
33+
if (!Object.keys(imports).length)
34+
return
35+
36+
const originalContent = existsSync(filepath) ? await fs.readFile(filepath, 'utf-8') : ''
37+
38+
const lines = Object.entries(imports)
39+
.sort((a, b) => a[0].localeCompare(b[0]))
40+
41+
let code = `// generated by unplugin-vue-components
42+
// We suggest you to NOT commit this file into source control
43+
// Read more: https://github.com/antfu/unplugin-vue-components/issues/135
44+
`
45+
46+
if (getVueVersion() === 'vue3') {
47+
code += `import { createApp } from "vue";
48+
49+
${lines.map(line => line[1]).join('\n')}
50+
51+
const app = createApp({});
52+
53+
${lines.map(line => `app.component('${line[0]}', ${line[0]})`).join('\n')}
54+
55+
app.mount("body");
56+
57+
`
58+
}
59+
else {
60+
code += `import Vue from "vue";
61+
62+
${lines.map(line => line[1]).join('\n')}
63+
64+
${lines.map(line => `Vue.component('${line[0]}', ${line[0]});\nVue.component('Lazy${line[0]}', ${line[0]});`).join('\n')}
65+
66+
`
67+
}
68+
69+
if (code !== originalContent)
70+
await fs.writeFile(filepath, code, 'utf-8')
71+
}

src/core/options.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export const defaultOptions: Omit<Required<Options>, 'include' | 'exclude' | 'tr
1919
importPathTransform: v => v,
2020

2121
allowOverrides: false,
22+
23+
generateIdeHelper: false,
2224
}
2325

2426
function normalizeResolvers(resolvers: (ComponentResolver | ComponentResolver[])[]): ComponentResolverObject[] {
@@ -60,6 +62,16 @@ export function resolveOptions(options: Options, root: string): ResolvedOptions
6062
? options.dts
6163
: 'components.d.ts',
6264
)
65+
66+
resolved.generateIdeHelper = !options.generateIdeHelper
67+
? false
68+
: resolve(
69+
root,
70+
typeof options.generateIdeHelper === 'string'
71+
? options.generateIdeHelper
72+
: '.components.gen.js',
73+
)
74+
6375
resolved.root = root
6476
resolved.transformer = options.transformer || getVueVersion() || 'vue3'
6577
resolved.directives = (typeof options.directives === 'boolean')
@@ -71,7 +83,7 @@ export function resolveOptions(options: Options, root: string): ResolvedOptions
7183
return resolved
7284
}
7385

74-
function getVueVersion() {
86+
export function getVueVersion() {
7587
try {
7688
// eslint-disable-next-line @typescript-eslint/no-var-requires
7789
const vue = require('vue')

src/core/transformer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export interface ResolveResult {
1515
replace: (resolved: string) => void
1616
}
1717

18-
export default function tranformer(ctx: Context, transformer: SupportedTransformer): Transformer {
18+
export default function transformer(ctx: Context, transformer: SupportedTransformer): Transformer {
1919
return async(code, id, path) => {
2020
ctx.searchGlob()
2121

src/core/unplugin.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export default createUnplugin<Options>((options = {}) => {
2626
try {
2727
const result = await ctx.transform(code, id)
2828
ctx.generateDeclaration()
29+
ctx.generateIdeHelper()
2930
return result
3031
}
3132
catch (e) {
@@ -44,6 +45,7 @@ export default createUnplugin<Options>((options = {}) => {
4445
if (options.dts) {
4546
ctx.searchGlob()
4647
ctx.generateDeclaration()
48+
ctx.generateIdeHelper()
4749
}
4850

4951
if (config.build.watch && config.command === 'build')

src/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,15 @@ export interface Options {
138138
* @default undefined
139139
*/
140140
directives?: boolean
141+
142+
/**
143+
* Generate components.js helper for IntelliJ IDEs.
144+
*
145+
* Accept boolean or a path related to project root.
146+
*
147+
* @default false
148+
*/
149+
generateIdeHelper?: boolean | string
141150
}
142151

143152
export type ResolvedOptions = Omit<
@@ -151,6 +160,7 @@ Required<Options>,
151160
resolvedDirs: string[]
152161
globs: string[]
153162
dts: string | false
163+
generateIdeHelper: string | false
154164
root: string
155165
}
156166

0 commit comments

Comments
 (0)