Skip to content

Commit

Permalink
feat(vue-code-gan): add .js when import vue file for esm support
Browse files Browse the repository at this point in the history
  • Loading branch information
yoyo930021 committed Jul 11, 2022
1 parent 5db6c43 commit c8cfd16
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 24 deletions.
155 changes: 131 additions & 24 deletions packages/vue-code-gen/src/generators/script.ts
Expand Up @@ -52,6 +52,7 @@ export function generate(

writeScriptSrc();
writeScriptSetupImports();
writeScriptImports();
writeScriptBeforeExportDefault();
writeScriptSetup();
writeScriptSetupTypes();
Expand Down Expand Up @@ -199,8 +200,25 @@ export function generate(
if (!script)
return;

function writeScriptRemoveImportRanges (end: number) {
if (scriptRanges?.imports.length) {
const removeTextRanges = scriptRanges?.imports?.map((r) => r.code).sort((a, b) => a.start - b.start);

removeTextRanges.forEach((range, index) => {
const prevRangeEnd = (index !== 0) ? removeTextRanges[index - 1].end : 0;
if (prevRangeEnd >= range.start) return
addVirtualCode('script', prevRangeEnd, range.start);
})
const lastRemoteTextRange = removeTextRanges[removeTextRanges.length - 1]
if (!lastRemoteTextRange || lastRemoteTextRange.end >= end) return
addVirtualCode('script', lastRemoteTextRange.end, end);
} else {
addVirtualCode('script', 0, end);
}
}

if (!!scriptSetup && scriptRanges?.exportDefault) {
addVirtualCode('script', 0, scriptRanges.exportDefault.expression.start);
writeScriptRemoveImportRanges(scriptRanges.exportDefault.expression.start);
exportdefaultStart = codeGen.getText().length - (scriptRanges.exportDefault.expression.start - scriptRanges.exportDefault.start);
}
else {
Expand All @@ -209,14 +227,14 @@ export function generate(
isExportRawObject = script.content.substring(scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end).startsWith('{');
}
if (isExportRawObject && shimComponentOptions && scriptRanges?.exportDefault) {
addVirtualCode('script', 0, scriptRanges.exportDefault.expression.start);
writeScriptRemoveImportRanges(scriptRanges.exportDefault.expression.start);
codeGen.addText(`(await import('${vueLibName}')).defineComponent(`);
addVirtualCode('script', scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end);
codeGen.addText(`)`);
addVirtualCode('script', scriptRanges.exportDefault.expression.end, script.content.length);
}
else {
addVirtualCode('script', 0, script.content.length);
writeScriptRemoveImportRanges(script.content.length);
}
}
}
Expand Down Expand Up @@ -267,29 +285,118 @@ export function generate(
if (!scriptSetup)
return;

if (!scriptSetupRanges)
if (!scriptSetupRanges?.imports.length)
return;

codeGen.addCode(
scriptSetup.content.substring(0, scriptSetupRanges.importSectionEndOffset),
{
start: 0,
end: scriptSetupRanges.importSectionEndOffset,
},
SourceMaps.Mode.Offset,
{
vueTag: 'scriptSetup',
capabilities: {
basic: lsType === 'script',
references: true,
definitions: lsType === 'script',
diagnostic: lsType === 'script',
rename: true,
completion: lsType === 'script',
semanticTokens: lsType === 'script',
},
},
);
for (const importRange of scriptSetupRanges.imports) {
const moduleName = scriptSetup.content.substring(importRange.moduleSpecifier.start, importRange.moduleSpecifier.end)
if (moduleName.endsWith('.vue\'') || moduleName.endsWith('.vue\"')) {
codeGen.addCode(
scriptSetup.content.substring(importRange.code.start, importRange.moduleSpecifier.start),
{
start: importRange.code.start,
end: importRange.moduleSpecifier.start,
},
SourceMaps.Mode.Offset,
{
vueTag: 'scriptSetup',
capabilities: {
basic: lsType === 'script',
references: true,
definitions: lsType === 'script',
diagnostic: lsType === 'script',
rename: true,
completion: lsType === 'script',
semanticTokens: lsType === 'script',
},
},
)
const newModuleName = moduleName.replace(/\.vue(['"])$/, '.vue.js$1')
codeGen.addCode(
newModuleName,
{
start: importRange.moduleSpecifier.start,
end: importRange.moduleSpecifier.end,
},
SourceMaps.Mode.Expand,
{
vueTag: 'scriptSetup',
capabilities: {
basic: lsType === 'script',
references: true,
definitions: lsType === 'script',
diagnostic: lsType === 'script',
rename: true,
completion: lsType === 'script',
semanticTokens: lsType === 'script',
},
},
)
addVirtualCode('scriptSetup', importRange.moduleSpecifier.end, importRange.code.end)
} else {
addVirtualCode('scriptSetup', importRange.code.start, importRange.code.end)
}
codeGen.addText(`\n`);
}
}
function writeScriptImports() {

if (!script)
return;

if (!scriptRanges?.imports.length)
return;

for (const importRange of scriptRanges.imports) {
const moduleName = script.content.substring(importRange.moduleSpecifier.start, importRange.moduleSpecifier.end)
if (moduleName.endsWith('.vue\'') || moduleName.endsWith('.vue\"')) {
codeGen.addCode(
script.content.substring(importRange.code.start, importRange.moduleSpecifier.start),
{
start: importRange.code.start,
end: importRange.moduleSpecifier.start,
},
SourceMaps.Mode.Offset,
{
vueTag: 'script',
capabilities: {
basic: lsType === 'script',
references: true,
definitions: lsType === 'script',
diagnostic: lsType === 'script',
rename: true,
completion: lsType === 'script',
semanticTokens: lsType === 'script',
},
},
)
const newModuleName = moduleName.replace(/.vue(['"])$/, '.vue.js$1')
codeGen.addCode(
newModuleName,
{
start: importRange.moduleSpecifier.start,
end: importRange.moduleSpecifier.end,
},
SourceMaps.Mode.Expand,
{
vueTag: 'script',
capabilities: {
basic: lsType === 'script',
references: true,
definitions: lsType === 'script',
diagnostic: lsType === 'script',
rename: true,
completion: lsType === 'script',
semanticTokens: lsType === 'script',
},
},
)
addVirtualCode('script', importRange.moduleSpecifier.end, importRange.code.end)
} else {
addVirtualCode('script', importRange.code.start, importRange.code.end)
}
codeGen.addText(`\n`);
}
}
function writeScriptSetup() {

Expand Down
35 changes: 35 additions & 0 deletions packages/vue-code-gen/src/parsers/scriptImportRanges.ts
@@ -0,0 +1,35 @@
import type * as ts from 'typescript/lib/tsserverlibrary';
import { getStartEnd } from './scriptSetupRanges';
import type { TextRange } from '../types';

export interface ScriptImportRanges extends ReturnType<typeof parseScriptImportRanges> { }

export function parseScriptImportRanges(ts: typeof import('typescript/lib/tsserverlibrary'), ast: ts.SourceFile, end: number) {

const calls: {
code: TextRange,
// import BaseText from './vue.js'
// ^^^^^^^^^^
moduleSpecifier: TextRange
}[] = [];

ast.forEachChild(node => {
visitNode(node);
});

return calls;

function visitNode(node: ts.Node) {
if (ts.isImportDeclaration(node)) {
const code = getStartEnd(node, ast)
if (code.start <= end) {
calls.push({
code: code,
moduleSpecifier: getStartEnd(node.moduleSpecifier, ast)
})
node.moduleSpecifier
}
}
node.forEachChild(child => visitNode(child));
}
}
2 changes: 2 additions & 0 deletions packages/vue-code-gen/src/parsers/scriptRanges.ts
@@ -1,5 +1,6 @@
import { getStartEnd, parseBindingRanges } from './scriptSetupRanges';
import type { TextRange } from '../types';
import { parseScriptImportRanges } from './scriptImportRanges';

export interface ScriptRanges extends ReturnType<typeof parseScriptRanges> { }

Expand Down Expand Up @@ -51,6 +52,7 @@ export function parseScriptRanges(ts: typeof import('typescript/lib/tsserverlibr
});

return {
imports: parseScriptImportRanges(ts, ast, ast.getEnd()),
exportDefault,
bindings,
};
Expand Down
2 changes: 2 additions & 0 deletions packages/vue-code-gen/src/parsers/scriptSetupRanges.ts
@@ -1,3 +1,4 @@
import { parseScriptImportRanges } from './scriptImportRanges';
import type * as ts from 'typescript/lib/tsserverlibrary';
import type { TextRange } from '../types';

Expand Down Expand Up @@ -42,6 +43,7 @@ export function parseScriptSetupRanges(ts: typeof import('typescript/lib/tsserve
ast.forEachChild(child => visitNode(child, ast));

return {
imports: parseScriptImportRanges(ts, ast, importSectionEndOffset),
importSectionEndOffset,
notOnTopTypeExports,
bindings,
Expand Down

0 comments on commit c8cfd16

Please sign in to comment.