Skip to content

Commit 43f182f

Browse files
authored
Merge pull request #242 from tkskto/feature/support-vue3
Feature/support vue3
2 parents 13868b6 + 4729fdf commit 43f182f

28 files changed

+1934
-68
lines changed

package-lock.json

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

package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
"vca": "bin/analyze.js"
1010
},
1111
"scripts": {
12-
"dev": "tsc -p src/server/tsconfig.json -w",
12+
"dev": "run-p dev:*",
13+
"dev:typeScript": "tsc -p src/server/tsconfig.json -w",
14+
"dev:rollup": "rollup --config -w",
1315
"build": "tsc -p src/server/tsconfig.json && rollup --config",
1416
"lint": "eslint -c .eslintrc.json src",
1517
"cover": "npm run cover:test && npm run cover:report",
@@ -46,6 +48,7 @@
4648
"access": "public"
4749
},
4850
"dependencies": {
51+
"@typescript-eslint/parser": "5.45.0",
4952
"commander": "9.4.1",
5053
"ejs": "3.1.8",
5154
"express": "4.18.2",
@@ -60,8 +63,8 @@
6063
"@rollup/plugin-commonjs": "24.0.0",
6164
"@rollup/plugin-json": "6.0.0",
6265
"@rollup/plugin-node-resolve": "15.0.1",
63-
"@rollup/plugin-typescript": "10.0.1",
6466
"@rollup/plugin-terser": "0.2.1",
67+
"@rollup/plugin-typescript": "10.0.1",
6568
"@types/ejs": "3.1.1",
6669
"@types/express": "4.17.15",
6770
"@types/jest": "29.2.4",
@@ -70,11 +73,11 @@
7073
"@types/opener": "1.4.0",
7174
"@types/ws": "8.5.3",
7275
"@typescript-eslint/eslint-plugin": "5.47.0",
73-
"@typescript-eslint/parser": "5.47.0",
7476
"eslint": "8.30.0",
7577
"husky": "8.0.2",
7678
"jest": "29.3.1",
7779
"lint-staged": "13.1.0",
80+
"npm-run-all": "^4.1.5",
7881
"nyc": "15.1.0",
7982
"rollup": "3.8.1",
8083
"rollup-plugin-license": "3.0.1",

src/server/Analyzer.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const getImportDeclarationTree = (fileName: string, parents: string[] = [
2929
}
3030

3131
// increment count of this file.
32-
fileCounter.add(shortFilename);
32+
fileCounter.add(filename);
3333

3434
// if this file is not Vue Component file, return only filename and stat.
3535
if (extname(filename) === '' || extname(filename) !== '.vue') {
@@ -44,6 +44,17 @@ export const getImportDeclarationTree = (fileName: string, parents: string[] = [
4444

4545
const contents = readFileSync(filename, 'utf-8');
4646
const component = new VueComponent(shortFilename, contents, stat);
47+
const getNextDeclaration = (source: string) => {
48+
const nextFilename = resolveFile(source, filename);
49+
50+
if (nextFilename) {
51+
if (parents.includes(nextFilename)) {
52+
console.warn(`Circular dependency detected between ${nextFilename} and ${filename}`);
53+
} else {
54+
component.addChildReport(getImportDeclarationTree(nextFilename, ancestorList, isTest));
55+
}
56+
}
57+
};
4758

4859
try {
4960
// if we get, read imported file recursive.
@@ -52,17 +63,13 @@ export const getImportDeclarationTree = (fileName: string, parents: string[] = [
5263
const source = String(component.importDeclaration[i].source.value);
5364

5465
if (source) {
55-
const nextFilename = resolveFile(source, filename);
56-
57-
if (nextFilename) {
58-
if (parents.includes(nextFilename)) {
59-
console.warn(`Circular dependency detected between ${nextFilename} and ${filename}`);
60-
} else {
61-
component.addChildReport(getImportDeclarationTree(nextFilename, ancestorList, isTest));
62-
}
63-
}
66+
getNextDeclaration(source);
6467
}
6568
}
69+
70+
if (component.srcAttribute) {
71+
getNextDeclaration(component.srcAttribute);
72+
}
6673
} catch (err) {
6774
console.error(`Something went wrong with reading ${filename}`);
6875
if (err instanceof Error) {

src/server/FileCounter.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
import {resolve} from 'path';
1+
const cwd = process.cwd();
22

33
export class FileCounter {
44
private _count: {[key: string]: number} = {};
55

66
public add(_filename: string): void {
7-
const filename = resolve(_filename);
7+
const shortFilename = _filename.replace(cwd, '');
88

9-
if (Object.prototype.hasOwnProperty.call(this._count, filename)) {
10-
this._count[filename]++;
9+
if (Object.prototype.hasOwnProperty.call(this._count, shortFilename)) {
10+
this._count[shortFilename]++;
1111
} else {
12-
this._count[filename] = 1;
12+
this._count[shortFilename] = 1;
1313
}
1414
}
1515

src/server/VueComponent.ts

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {Stats} from 'fs';
88
const parserOption = {
99
ecmaVersion: 'latest',
1010
sourceType: 'module',
11+
parser: '@typescript-eslint/parser',
1112
};
1213

1314
export class VueComponent {
@@ -27,30 +28,44 @@ export class VueComponent {
2728

2829
private _importDeclaration: ESLintImportDeclaration[] = [];
2930

31+
private _srcAttribute: string;
32+
3033
constructor(filename: string, contents: string, stats?: Stats) {
3134
this._filename = filename;
3235
this._lastModifiedTime = stats?.mtimeMs || 0;
3336
this._size = stats?.size || 0;
3437

3538
// get each part from text of file.
3639
const templateBody = contents.match(/(?<template><template>[\s\S]*<\/template>)/u);
37-
const scriptBody = contents.match(/(?<script><script>[\s\S]*<\/script>)/u);
40+
41+
// support src, lang, setup attribute.
42+
const scriptBody = contents.match(/(?<script><script[\s\S]*>[\s\S]*<\/script>)/u);
3843
const styleBody = contents.match(/(?<style><style>[\s\S]*<\/style>)/u);
3944

4045
this._template = templateBody?.groups?.template || '';
41-
this._style = styleBody?.groups?.template || '';
46+
this._style = styleBody?.groups?.style || '';
4247

4348
const scriptString = scriptBody?.groups?.script || '';
4449

45-
// using vue-eslint-parser package.
46-
const esLintProgram: ESLintProgram = parse(scriptString, parserOption);
50+
try {
51+
// using vue-eslint-parser package.
52+
const esLintProgram: ESLintProgram = parse(scriptString, parserOption);
53+
54+
// get props from parser results.
55+
if (esLintProgram.tokens) {
56+
this._props = this.getProps(esLintProgram.tokens);
57+
}
58+
59+
this._importDeclaration = getImportDeclaration(esLintProgram.body);
60+
} catch (err: unknown) {
61+
console.error('something went wrong at pars.');
4762

48-
// get props from parser results.
49-
if (esLintProgram.tokens) {
50-
this._props = this.getProps(esLintProgram.tokens);
63+
throw err;
5164
}
5265

53-
this._importDeclaration = getImportDeclaration(esLintProgram.body);
66+
const scriptSrc = scriptString.match(/<script src="(?<src>[\s\S]*)">/u);
67+
68+
this._srcAttribute = scriptSrc?.groups?.src || '';
5469
}
5570

5671
private getProps(tokens: Token[]): string {
@@ -61,10 +76,22 @@ export class VueComponent {
6176
return propsDeclaration.props;
6277
}
6378

79+
const definePropsDeclaration = getDeclarationSyntax(tokens, 'defineProps');
80+
81+
const definePropsDeclarationJSON = JSON.parse(definePropsDeclaration);
82+
83+
if (definePropsDeclarationJSON && definePropsDeclarationJSON.defineProps) {
84+
return definePropsDeclarationJSON.defineProps;
85+
}
86+
6487
return '';
6588
} catch (err) {
6689
console.warn('failed to analyze props.');
6790

91+
if (err instanceof Error) {
92+
console.error(err.message);
93+
}
94+
6895
return '';
6996
}
7097
}
@@ -77,6 +104,10 @@ export class VueComponent {
77104
return this._importDeclaration;
78105
}
79106

107+
get srcAttribute(): string {
108+
return this._srcAttribute;
109+
}
110+
80111
public getFileReport(isTest: boolean): FileReport {
81112
return {
82113
name: this._filename,

src/server/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ const analyze = (entries: string[]): Promise<AnalyzeReport> => {
2828
return new Promise((resolve, reject) => {
2929
try {
3030
if (entries.length === 0) {
31-
console.log('There is no entry file.');
31+
reject(new Error('There is no entry file.'));
32+
33+
return;
3234
}
3335

3436
const entriesData = [];
@@ -45,6 +47,7 @@ const analyze = (entries: string[]): Promise<AnalyzeReport> => {
4547
count: fileCounter.result,
4648
});
4749
} catch (err) {
50+
console.error('something went to wrong at analyze.');
4851
reject(err);
4952
}
5053
});

src/server/utils.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {resolve, extname, dirname} from 'path';
1212
* get only Import Declaration syntax.
1313
* @param {Node[]} nodeArr
1414
*/
15-
export const getImportDeclaration = (nodeArr: (ESLintStatement | ESLintModuleDeclaration)[]): ESLintImportDeclaration[] => { // eslint-disable-line
15+
export const getImportDeclaration = (nodeArr: (ESLintStatement | ESLintModuleDeclaration)[]): ESLintImportDeclaration[] => {
1616
return nodeArr.filter((node) => node.type === 'ImportDeclaration') as ESLintImportDeclaration[];
1717
};
1818

@@ -22,7 +22,7 @@ export const getImportDeclaration = (nodeArr: (ESLintStatement | ESLintModuleDec
2222
* @param targetKeyName
2323
* @returns {string}
2424
*/
25-
export const getDeclarationSyntax = (tokens: Token[], targetKeyName: 'data' | 'props'): string => {
25+
export const getDeclarationSyntax = (tokens: Token[], targetKeyName: 'data' | 'props' | 'defineProps'): string => {
2626
let isTargetToken = false;
2727
let result = '{'; // for JSON.parse
2828
let closedCount = 0;
@@ -36,6 +36,10 @@ export const getDeclarationSyntax = (tokens: Token[], targetKeyName: 'data' | 'p
3636
const needQuoting = needQuotingTypes.includes(type);
3737
isTargetToken = true;
3838

39+
if (['(', ')'].includes(value)) {
40+
continue;
41+
}
42+
3943
if (type === 'Punctuator') {
4044
// count brace for finding end of the declaration.
4145
if (value === '{') {
@@ -61,12 +65,16 @@ export const getDeclarationSyntax = (tokens: Token[], targetKeyName: 'data' | 'p
6165
}
6266

6367
// change quotation to double for JSON.
64-
result += value.replace(/'/ug, '"');
68+
result += value.replaceAll('\'', '"');
6569

6670
// put right-hand quotation for JSON.
6771
if (needQuoting) {
6872
result += '"';
6973
}
74+
75+
if (value === 'defineProps') {
76+
result += ':';
77+
}
7078
}
7179
}
7280

test/assets/tsModule.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default {};

test/components/nested2Setup.vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<template>
2+
<div>nested2</div>
3+
</template>
4+
<script setup>
5+
import child from './child.vue';
6+
import child2 from './child2.vue';
7+
</script>

test/components/nestedSetup.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<template>
2+
<div>nested</div>
3+
</template>
4+
<script setup>
5+
import child from './child.vue';
6+
</script>

0 commit comments

Comments
 (0)