Phase 1 MVP - Complete implementation

- Login with JWT and refresh token rotation
- Dashboard with projects cards
- ProjectView with TreeView navigation
- DocumentView with markdown editor and auto-save
- Tag management (create, assign, remove)
- Dark mode CSS variables
- Security fixes applied (logout to backend, createDocument endpoint)
This commit is contained in:
Motoko
2026-03-30 15:17:29 +00:00
commit c9cb07dbfc
1341 changed files with 1084068 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
import type { VueLanguagePlugin } from '../types';
declare const plugin: VueLanguagePlugin;
export default plugin;

View File

@@ -0,0 +1,90 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const sfcBlockReg = /\<(script|style)\b([\s\S]*?)\>([\s\S]*?)\<\/\1\>/g;
const langReg = /\blang\s*=\s*(['\"]?)(\S*)\b\1/;
const plugin = ({ vueCompilerOptions }) => {
return {
version: 2.1,
getLanguageId(fileName) {
if (vueCompilerOptions.petiteVueExtensions.some(ext => fileName.endsWith(ext))) {
return 'html';
}
},
isValidFile(_fileName, languageId) {
return languageId === 'html';
},
parseSFC2(fileName, languageId, content) {
if (languageId !== 'html') {
return;
}
let sfc = {
descriptor: {
filename: fileName,
source: content,
comments: [],
template: null,
script: null,
scriptSetup: null,
styles: [],
customBlocks: [],
cssVars: [],
shouldForceReload: () => false,
slotted: false,
},
errors: [],
};
let templateContent = content;
for (const match of content.matchAll(sfcBlockReg)) {
const matchText = match[0];
const tag = match[1];
const attrs = match[2];
const lang = attrs.match(langReg)?.[2];
const content = match[3];
const contentStart = match.index + matchText.indexOf(content);
if (tag === 'style') {
sfc.descriptor.styles.push({
attrs: {},
content,
loc: {
start: { column: -1, line: -1, offset: contentStart },
end: { column: -1, line: -1, offset: contentStart + content.length },
source: content,
},
type: 'style',
lang,
});
}
// ignore `<script src="...">`
else if (tag === 'script' && !attrs.includes('src=')) {
let type = attrs.includes('type=') ? 'scriptSetup' : 'script';
sfc.descriptor[type] = {
attrs: {},
content,
loc: {
start: { column: -1, line: -1, offset: contentStart },
end: { column: -1, line: -1, offset: contentStart + content.length },
source: content,
},
type: 'script',
lang,
};
}
templateContent = templateContent.slice(0, match.index) + ' '.repeat(matchText.length) + templateContent.slice(match.index + matchText.length);
}
sfc.descriptor.template = {
attrs: {},
content: templateContent,
loc: {
start: { column: -1, line: -1, offset: 0 },
end: { column: -1, line: -1, offset: templateContent.length },
source: templateContent,
},
type: 'template',
ast: {},
};
return sfc;
}
};
};
exports.default = plugin;
//# sourceMappingURL=file-html.js.map

View File

@@ -0,0 +1,3 @@
import type { VueLanguagePlugin } from '../types';
declare const plugin: VueLanguagePlugin;
export default plugin;

96
node_modules/@vue/language-core/lib/plugins/file-md.js generated vendored Normal file
View File

@@ -0,0 +1,96 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const language_core_1 = require("@volar/language-core");
const muggle_string_1 = require("muggle-string");
const buildMappings_1 = require("../utils/buildMappings");
const parseSfc_1 = require("../utils/parseSfc");
const codeblockReg = /(`{3,})[\s\S]+?\1/g;
const inlineCodeblockReg = /`[^\n`]+?`/g;
const latexBlockReg = /(\${2,})[\s\S]+?\1/g;
const scriptSetupReg = /\\\<[\s\S]+?\>\n?/g;
const sfcBlockReg = /\<(script|style)\b[\s\S]*?\>([\s\S]*?)\<\/\1\>/g;
const angleBracketReg = /\<\S*\:\S*\>/g;
const linkReg = /\[[\s\S]*?\]\([\s\S]*?\)/g;
const codeSnippetImportReg = /^\s*<<<\s*.+/gm;
const plugin = ({ vueCompilerOptions }) => {
return {
version: 2.1,
getLanguageId(fileName) {
if (vueCompilerOptions.vitePressExtensions.some(ext => fileName.endsWith(ext))) {
return 'markdown';
}
},
isValidFile(_fileName, languageId) {
return languageId === 'markdown';
},
parseSFC2(_fileName, languageId, content) {
if (languageId !== 'markdown') {
return;
}
content = content
// code block
.replace(codeblockReg, (match, quotes) => quotes + ' '.repeat(match.length - quotes.length * 2) + quotes)
// inline code block
.replace(inlineCodeblockReg, match => `\`${' '.repeat(match.length - 2)}\``)
// latex block
.replace(latexBlockReg, (match, quotes) => quotes + ' '.repeat(match.length - quotes.length * 2) + quotes)
// # \<script setup>
.replace(scriptSetupReg, match => ' '.repeat(match.length))
// <<< https://vitepress.dev/guide/markdown#import-code-snippets
.replace(codeSnippetImportReg, match => ' '.repeat(match.length));
const codes = [];
for (const match of content.matchAll(sfcBlockReg)) {
if (match.index !== undefined) {
const matchText = match[0];
codes.push([matchText, undefined, match.index]);
codes.push('\n\n');
content = content.slice(0, match.index) + ' '.repeat(matchText.length) + content.slice(match.index + matchText.length);
}
}
content = content
// angle bracket: <http://foo.com>
.replace(angleBracketReg, match => ' '.repeat(match.length))
// [foo](http://foo.com)
.replace(linkReg, match => ' '.repeat(match.length));
codes.push('<template>\n');
codes.push([content, undefined, 0]);
codes.push('\n</template>');
const file2VueSourceMap = (0, language_core_1.defaultMapperFactory)((0, buildMappings_1.buildMappings)(codes));
const sfc = (0, parseSfc_1.parse)((0, muggle_string_1.toString)(codes));
if (sfc.descriptor.template) {
sfc.descriptor.template.lang = 'md';
transformRange(sfc.descriptor.template);
}
if (sfc.descriptor.script) {
transformRange(sfc.descriptor.script);
}
if (sfc.descriptor.scriptSetup) {
transformRange(sfc.descriptor.scriptSetup);
}
for (const style of sfc.descriptor.styles) {
transformRange(style);
}
for (const customBlock of sfc.descriptor.customBlocks) {
transformRange(customBlock);
}
return sfc;
function transformRange(block) {
const { start, end } = block.loc;
const startOffset = start.offset;
const endOffset = end.offset;
start.offset = -1;
end.offset = -1;
for (const [offset] of file2VueSourceMap.toSourceLocation(startOffset)) {
start.offset = offset;
break;
}
for (const [offset] of file2VueSourceMap.toSourceLocation(endOffset)) {
end.offset = offset;
break;
}
}
}
};
};
exports.default = plugin;
//# sourceMappingURL=file-md.js.map

View File

@@ -0,0 +1,3 @@
import type { VueLanguagePlugin } from '../types';
declare const plugin: VueLanguagePlugin;
export default plugin;

View File

@@ -0,0 +1,58 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const parseSfc_1 = require("../utils/parseSfc");
const plugin = ({ vueCompilerOptions }) => {
return {
version: 2.1,
getLanguageId(fileName) {
if (vueCompilerOptions.extensions.some(ext => fileName.endsWith(ext))) {
return 'vue';
}
},
isValidFile(_fileName, languageId) {
return languageId === 'vue';
},
parseSFC2(_fileName, languageId, content) {
if (languageId !== 'vue') {
return;
}
return (0, parseSfc_1.parse)(content);
},
updateSFC(sfc, change) {
const blocks = [
sfc.descriptor.template,
sfc.descriptor.script,
sfc.descriptor.scriptSetup,
...sfc.descriptor.styles,
...sfc.descriptor.customBlocks,
].filter(block => !!block);
const hitBlock = blocks.find(block => change.start >= block.loc.start.offset && change.end <= block.loc.end.offset);
if (!hitBlock) {
return;
}
const oldContent = hitBlock.content;
const newContent = hitBlock.content =
hitBlock.content.slice(0, change.start - hitBlock.loc.start.offset)
+ change.newText
+ hitBlock.content.slice(change.end - hitBlock.loc.start.offset);
// #3449
const endTagRegex = new RegExp(`</\\s*${hitBlock.type}\\s*>`);
const insertedEndTag = endTagRegex.test(oldContent) !== endTagRegex.test(newContent);
if (insertedEndTag) {
return;
}
const lengthDiff = change.newText.length - (change.end - change.start);
for (const block of blocks) {
if (block.loc.start.offset > change.end) {
block.loc.start.offset += lengthDiff;
}
if (block.loc.end.offset >= change.end) {
block.loc.end.offset += lengthDiff;
}
}
return sfc;
},
};
};
exports.default = plugin;
//# sourceMappingURL=file-vue.js.map

View File

@@ -0,0 +1,2 @@
import type { CodeInformation } from '@volar/language-core';
export declare const allCodeFeatures: CodeInformation;

12
node_modules/@vue/language-core/lib/plugins/shared.js generated vendored Normal file
View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.allCodeFeatures = void 0;
exports.allCodeFeatures = {
verification: true,
completion: true,
semantic: true,
navigation: true,
structure: true,
format: true,
};
//# sourceMappingURL=shared.js.map

View File

@@ -0,0 +1,3 @@
import type { VueLanguagePlugin } from '../types';
declare const plugin: VueLanguagePlugin;
export default plugin;

View File

@@ -0,0 +1,51 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const muggle_string_1 = require("muggle-string");
const shared_1 = require("./shared");
const plugin = () => {
return {
version: 2.1,
getEmbeddedCodes() {
return [{
id: 'root_tags',
lang: 'vue-root-tags',
}];
},
resolveEmbeddedCode(_fileName, sfc, embeddedFile) {
if (embeddedFile.id === 'root_tags') {
embeddedFile.content.push([sfc.content, undefined, 0, shared_1.allCodeFeatures]);
for (const block of [
sfc.script,
sfc.scriptSetup,
sfc.template,
...sfc.styles,
...sfc.customBlocks,
]) {
if (!block) {
continue;
}
let content = block.content;
if (content.endsWith('\r\n')) {
content = content.slice(0, -2);
}
else if (content.endsWith('\n')) {
content = content.slice(0, -1);
}
const offset = content.lastIndexOf('\n') + 1;
// fix folding range end position failed to mapping
(0, muggle_string_1.replaceSourceRange)(embeddedFile.content, undefined, block.startTagEnd, block.endTagStart, sfc.content.slice(block.startTagEnd, block.startTagEnd + offset), [
'',
undefined,
block.startTagEnd + offset,
{ structure: true },
], sfc.content.slice(block.startTagEnd + offset, block.endTagStart));
}
}
else {
embeddedFile.parentCodeId ??= 'root_tags';
}
},
};
};
exports.default = plugin;
//# sourceMappingURL=vue-root-tags.js.map

View File

@@ -0,0 +1,3 @@
import type { VueLanguagePlugin } from '../types';
declare const plugin: VueLanguagePlugin;
export default plugin;

View File

@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const plugin = ({ modules }) => {
return {
version: 2.1,
compileSFCScript(lang, script) {
if (lang === 'js' || lang === 'ts' || lang === 'jsx' || lang === 'tsx') {
const ts = modules.typescript;
return ts.createSourceFile('test.' + lang, script, 99);
}
},
};
};
exports.default = plugin;
//# sourceMappingURL=vue-script-js.js.map

View File

@@ -0,0 +1,3 @@
import type { VueLanguagePlugin } from '../types';
declare const plugin: VueLanguagePlugin;
export default plugin;

View File

@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const shared_1 = require("./shared");
const plugin = () => {
return {
version: 2.1,
getEmbeddedCodes(_fileName, sfc) {
return sfc.customBlocks.map((customBlock, i) => ({
id: 'custom_block_' + i,
lang: customBlock.lang,
}));
},
resolveEmbeddedCode(_fileName, sfc, embeddedFile) {
if (embeddedFile.id.startsWith('custom_block_')) {
const index = parseInt(embeddedFile.id.slice('custom_block_'.length));
const customBlock = sfc.customBlocks[index];
embeddedFile.content.push([
customBlock.content,
customBlock.name,
0,
shared_1.allCodeFeatures,
]);
}
},
};
};
exports.default = plugin;
//# sourceMappingURL=vue-sfc-customblocks.js.map

View File

@@ -0,0 +1,3 @@
import type { VueLanguagePlugin } from '../types';
declare const plugin: VueLanguagePlugin;
export default plugin;

View File

@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const plugin = () => {
return {
version: 2.1,
getEmbeddedCodes(_fileName, sfc) {
const names = [];
if (sfc.script) {
names.push({ id: 'script_raw', lang: sfc.script.lang });
}
if (sfc.scriptSetup) {
names.push({ id: 'scriptsetup_raw', lang: sfc.scriptSetup.lang });
}
return names;
},
resolveEmbeddedCode(_fileName, sfc, embeddedFile) {
const script = embeddedFile.id === 'script_raw' ? sfc.script
: embeddedFile.id === 'scriptsetup_raw' ? sfc.scriptSetup
: undefined;
if (script) {
embeddedFile.content.push([
script.content,
script.name,
0,
{
structure: true,
format: true,
},
]);
}
},
};
};
exports.default = plugin;
//# sourceMappingURL=vue-sfc-scripts.js.map

View File

@@ -0,0 +1,3 @@
import type { VueLanguagePlugin } from '../types';
declare const plugin: VueLanguagePlugin;
export default plugin;

View File

@@ -0,0 +1,54 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const shared_1 = require("./shared");
const plugin = () => {
return {
version: 2.1,
getEmbeddedCodes(_fileName, sfc) {
const result = [];
for (let i = 0; i < sfc.styles.length; i++) {
const style = sfc.styles[i];
if (style) {
result.push({
id: 'style_' + i,
lang: style.lang,
});
if (style.cssVars.length) {
result.push({
id: 'style_' + i + '_inline_ts',
lang: 'ts',
});
}
}
}
return result;
},
resolveEmbeddedCode(_fileName, sfc, embeddedFile) {
if (embeddedFile.id.startsWith('style_')) {
const index = parseInt(embeddedFile.id.split('_')[1]);
const style = sfc.styles[index];
if (embeddedFile.id.endsWith('_inline_ts')) {
embeddedFile.parentCodeId = 'style_' + index;
for (const cssVar of style.cssVars) {
embeddedFile.content.push('(', [
cssVar.text,
style.name,
cssVar.offset,
shared_1.allCodeFeatures,
], ');\n');
}
}
else {
embeddedFile.content.push([
style.content,
style.name,
0,
shared_1.allCodeFeatures,
]);
}
}
},
};
};
exports.default = plugin;
//# sourceMappingURL=vue-sfc-styles.js.map

View File

@@ -0,0 +1,3 @@
import type { VueLanguagePlugin } from '../types';
declare const plugin: VueLanguagePlugin;
export default plugin;

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const shared_1 = require("./shared");
const plugin = () => {
return {
version: 2.1,
getEmbeddedCodes(_fileName, sfc) {
if (sfc.template?.lang === 'html') {
return [{
id: 'template',
lang: sfc.template.lang,
}];
}
return [];
},
resolveEmbeddedCode(_fileName, sfc, embeddedFile) {
if (embeddedFile.id === 'template' && sfc.template?.lang === 'html') {
embeddedFile.content.push([
sfc.template.content,
sfc.template.name,
0,
shared_1.allCodeFeatures,
]);
}
},
};
};
exports.default = plugin;
//# sourceMappingURL=vue-sfc-template.js.map

View File

@@ -0,0 +1,3 @@
import type { VueLanguagePlugin } from '../types';
declare const plugin: VueLanguagePlugin;
export default plugin;

View File

@@ -0,0 +1,201 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const shouldAddSuffix = /(?<=<[^>/]+)$/;
const plugin = ({ modules }) => {
return {
version: 2.1,
compileSFCTemplate(lang, template, options) {
if (lang === 'html' || lang === 'md') {
const compiler = modules['@vue/compiler-dom'];
let addedSuffix = false;
// #4583
if (shouldAddSuffix.test(template)) {
template += '>';
addedSuffix = true;
}
const result = compiler.compile(template, {
...options,
comments: true,
});
// @ts-expect-error
result.__addedSuffix = addedSuffix;
return result;
}
},
updateSFCTemplate(oldResult, change) {
oldResult.code = oldResult.code.slice(0, change.start)
+ change.newText
+ oldResult.code.slice(change.end);
// @ts-expect-error
if (oldResult.__addedSuffix) {
const originalTemplate = oldResult.code.slice(0, -1); // remove added '>'
if (!shouldAddSuffix.test(originalTemplate)) {
return undefined;
}
}
const CompilerDOM = modules['@vue/compiler-dom'];
const lengthDiff = change.newText.length - (change.end - change.start);
let hitNodes = [];
if (tryUpdateNode(oldResult.ast) && hitNodes.length) {
hitNodes = hitNodes.sort((a, b) => a.loc.source.length - b.loc.source.length);
const hitNode = hitNodes[0];
if (hitNode.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
return oldResult;
}
}
function tryUpdateNode(node) {
if (withinChangeRange(node.loc)) {
hitNodes.push(node);
}
if (tryUpdateNodeLoc(node.loc)) {
if (node.type === CompilerDOM.NodeTypes.ROOT) {
for (const child of node.children) {
if (!tryUpdateNode(child)) {
return false;
}
}
}
else if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
if (withinChangeRange(node.loc)) {
// if not self closing, should not hit tag name
const start = node.loc.start.offset + 2;
const end = node.loc.start.offset + node.loc.source.lastIndexOf('</');
if (!withinChangeRange({ start: { offset: start }, end: { offset: end }, source: '' })) {
return false;
}
}
for (const prop of node.props) {
if (!tryUpdateNode(prop)) {
return false;
}
}
for (const child of node.children) {
if (!tryUpdateNode(child)) {
return false;
}
}
}
else if (node.type === CompilerDOM.NodeTypes.ATTRIBUTE) {
if (node.value && !tryUpdateNode(node.value)) {
return false;
}
}
else if (node.type === CompilerDOM.NodeTypes.DIRECTIVE) {
if (node.arg && withinChangeRange(node.arg.loc) && node.name === 'slot') {
return false;
}
if (node.exp && withinChangeRange(node.exp.loc) && node.name === 'for') { // #2266
return false;
}
if (node.arg && !tryUpdateNode(node.arg)) {
return false;
}
if (node.exp && !tryUpdateNode(node.exp)) {
return false;
}
}
else if (node.type === CompilerDOM.NodeTypes.TEXT_CALL) {
if (!tryUpdateNode(node.content)) {
return false;
}
}
else if (node.type === CompilerDOM.NodeTypes.COMPOUND_EXPRESSION) {
for (const childNode of node.children) {
if (typeof childNode === 'object') {
if (!tryUpdateNode(childNode)) {
return false;
}
}
}
}
else if (node.type === CompilerDOM.NodeTypes.IF) {
for (const branch of node.branches) {
if (branch.condition && !tryUpdateNode(branch.condition)) {
return false;
}
for (const child of branch.children) {
if (!tryUpdateNode(child)) {
return false;
}
}
}
}
else if (node.type === CompilerDOM.NodeTypes.FOR) {
for (const child of [
node.parseResult.source,
node.parseResult.value,
node.parseResult.key,
node.parseResult.index,
]) {
if (child) {
if (!tryUpdateNode(child)) {
return false;
}
if (child.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
const content = child.content.trim();
if (content.startsWith('(') || content.endsWith(')')) {
return false;
}
}
}
}
for (const child of node.children) {
if (!tryUpdateNode(child)) {
return false;
}
}
}
else if (node.type === CompilerDOM.NodeTypes.INTERPOLATION) {
if (!tryUpdateNode(node.content)) {
return false;
}
}
else if (node.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
if (withinChangeRange(node.loc)) { // TODO: review this (slot name?)
if (node.isStatic) {
return false;
}
else if (!node.loc.source) {
// :class="..." -> :class=""
return false;
}
else {
node.content = node.loc.source;
}
}
}
return true;
}
return false;
}
function tryUpdateNodeLoc(loc) {
delete loc.__endOffset;
if (withinChangeRange(loc)) {
loc.source =
loc.source.slice(0, change.start - loc.start.offset)
+ change.newText
+ loc.source.slice(change.end - loc.start.offset);
loc.__endOffset = loc.end.offset;
loc.end.offset += lengthDiff;
return true;
}
else if (change.end <= loc.start.offset) {
loc.__endOffset = loc.end.offset;
loc.start.offset += lengthDiff;
loc.end.offset += lengthDiff;
return true;
}
else if (change.start >= loc.end.offset) {
return true; // no need update
}
return false;
}
function withinChangeRange(loc) {
const originalLocEnd = loc.__endOffset ?? loc.end.offset;
return change.start >= loc.start.offset && change.end <= originalLocEnd;
}
},
};
};
exports.default = plugin;
//# sourceMappingURL=vue-template-html.js.map

View File

@@ -0,0 +1,3 @@
import type { VueLanguagePlugin } from '../types';
declare const plugin: VueLanguagePlugin;
export default plugin;

View File

@@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const CompilerDOM = require("@vue/compiler-dom");
const template_1 = require("../codegen/template");
const shared_1 = require("./shared");
const codeFeatures = {
...shared_1.allCodeFeatures,
format: false,
structure: false,
};
const plugin = () => {
return {
version: 2.1,
getEmbeddedCodes(_fileName, sfc) {
if (!sfc.template?.ast) {
return [];
}
return [{ id: 'template_inline_css', lang: 'css' }];
},
resolveEmbeddedCode(_fileName, sfc, embeddedFile) {
if (embeddedFile.id !== 'template_inline_css' || !sfc.template?.ast) {
return;
}
embeddedFile.parentCodeId = 'template';
embeddedFile.content.push(...generate(sfc.template.ast));
},
};
};
exports.default = plugin;
function* generate(templateAst) {
for (const node of (0, template_1.forEachElementNode)(templateAst)) {
for (const prop of node.props) {
if (prop.type === CompilerDOM.NodeTypes.DIRECTIVE
&& prop.name === 'bind'
&& prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
&& prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
&& prop.arg.content === 'style'
&& prop.exp.constType === CompilerDOM.ConstantTypes.CAN_STRINGIFY) {
const endCrt = prop.arg.loc.source[prop.arg.loc.source.length - 1]; // " | '
const start = prop.arg.loc.source.indexOf(endCrt) + 1;
const end = prop.arg.loc.source.lastIndexOf(endCrt);
const content = prop.arg.loc.source.slice(start, end);
yield `x { `;
yield [
content,
'template',
prop.arg.loc.start.offset + start,
codeFeatures,
];
yield ` }\n`;
}
}
}
}
//# sourceMappingURL=vue-template-inline-css.js.map

View File

@@ -0,0 +1,3 @@
import type { VueLanguagePlugin } from '../types';
declare const plugin: VueLanguagePlugin;
export default plugin;

View File

@@ -0,0 +1,201 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const CompilerDOM = require("@vue/compiler-dom");
const elementEvents_1 = require("../codegen/template/elementEvents");
const templateChild_1 = require("../codegen/template/templateChild");
const vFor_1 = require("../codegen/template/vFor");
const utils_1 = require("../codegen/utils");
const codeFeatures = {
format: true,
};
const formatBrackets = {
normal: ['`${', '}`;'],
if: ['if (', ') { }'],
for: ['for (', ') { }'],
// fix https://github.com/vuejs/language-tools/issues/3572
params: ['(', ') => {};'],
// fix https://github.com/vuejs/language-tools/issues/1210
// fix https://github.com/vuejs/language-tools/issues/2305
curly: ['0 +', '+ 0;'],
event: ['() => ', ';'],
generic: ['<', '>() => {};'],
};
const plugin = ctx => {
const parseds = new WeakMap();
return {
version: 2.1,
getEmbeddedCodes(_fileName, sfc) {
if (!sfc.template?.ast) {
return [];
}
const parsed = parse(sfc);
parseds.set(sfc, parsed);
const result = [];
for (const [id] of parsed) {
result.push({ id, lang: 'ts' });
}
return result;
},
resolveEmbeddedCode(_fileName, sfc, embeddedFile) {
// access template content to watch change
(() => sfc.template?.content)();
const parsed = parseds.get(sfc);
if (parsed) {
const codes = parsed.get(embeddedFile.id);
if (codes) {
embeddedFile.content.push(...codes);
embeddedFile.parentCodeId = 'template';
}
}
},
};
function parse(sfc) {
const data = new Map();
if (!sfc.template?.ast) {
return data;
}
const templateContent = sfc.template.content;
let i = 0;
sfc.template.ast.children.forEach(visit);
return data;
function visit(node) {
if (node.type === CompilerDOM.NodeTypes.COMMENT) {
const match = node.loc.source.match(/^<!--\s*@vue-generic\s*\{(?<content>[\s\S]*)\}\s*-->$/);
if (match) {
const { content } = match.groups;
addFormatCodes(content, node.loc.start.offset + node.loc.source.indexOf('{') + 1, formatBrackets.generic);
}
}
else if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
for (const prop of node.props) {
if (prop.type !== CompilerDOM.NodeTypes.DIRECTIVE) {
continue;
}
const isShorthand = prop.arg?.loc.start.offset === prop.exp?.loc.start.offset; // vue 3.4+
if (isShorthand) {
continue;
}
if (prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && !prop.arg.isStatic) {
addFormatCodes(prop.arg.loc.source, prop.arg.loc.start.offset, formatBrackets.normal);
}
if (prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
&& prop.exp.constType !== CompilerDOM.ConstantTypes.CAN_STRINGIFY // style='z-index: 2' will compile to {'z-index':'2'}
) {
if (prop.name === 'on' && prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
const ast = (0, utils_1.createTsAst)(ctx.modules.typescript, prop.exp, prop.exp.content);
if ((0, elementEvents_1.isCompoundExpression)(ctx.modules.typescript, ast)) {
addFormatCodes(prop.exp.loc.source, prop.exp.loc.start.offset, formatBrackets.event);
}
else {
const lines = prop.exp.content.split('\n');
const firstLineEmpty = lines[0].trim() === '';
const lastLineEmpty = lines[lines.length - 1].trim() === '';
if (lines.length <= 1 || (!firstLineEmpty && !lastLineEmpty)) {
addFormatCodes(prop.exp.loc.source, prop.exp.loc.start.offset, formatBrackets.normal);
}
else {
addFormatCodes(prop.exp.loc.source, prop.exp.loc.start.offset, ['(', ');']);
}
}
}
else if (prop.name === 'slot') {
addFormatCodes(prop.exp.loc.source, prop.exp.loc.start.offset, formatBrackets.params);
}
else if (prop.rawName === 'v-for') {
// #2586
addFormatCodes(prop.exp.loc.source, prop.exp.loc.start.offset, formatBrackets.for);
}
else {
addFormatCodes(prop.exp.loc.source, prop.exp.loc.start.offset, formatBrackets.normal);
}
}
}
for (const child of node.children) {
visit(child);
}
}
else if (node.type === CompilerDOM.NodeTypes.IF) {
for (let i = 0; i < node.branches.length; i++) {
const branch = node.branches[i];
if (branch.condition?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
addFormatCodes(branch.condition.loc.source, branch.condition.loc.start.offset, formatBrackets.if);
}
for (const childNode of branch.children) {
visit(childNode);
}
}
}
else if (node.type === CompilerDOM.NodeTypes.FOR) {
const { leftExpressionRange, leftExpressionText } = (0, vFor_1.parseVForNode)(node);
const { source } = node.parseResult;
if (leftExpressionRange && leftExpressionText && source.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
let start = leftExpressionRange.start;
let end = source.loc.start.offset + source.content.length;
while (templateContent[start - 1] === ' ' || templateContent[start - 1] === '(') {
start--;
}
while (templateContent[end] === ' ' || templateContent[end] === ')') {
end++;
}
addFormatCodes(templateContent.slice(start, end), start, formatBrackets.for);
}
for (const child of node.children) {
visit(child);
}
}
else if (node.type === CompilerDOM.NodeTypes.TEXT_CALL) {
// {{ var }}
visit(node.content);
}
else if (node.type === CompilerDOM.NodeTypes.COMPOUND_EXPRESSION) {
// {{ ... }} {{ ... }}
for (const childNode of node.children) {
if (typeof childNode === 'object') {
visit(childNode);
}
}
}
else if (node.type === CompilerDOM.NodeTypes.INTERPOLATION) {
// {{ ... }}
const [content, start] = (0, templateChild_1.parseInterpolationNode)(node, templateContent);
const lines = content.split('\n');
const firstLineEmpty = lines[0].trim() === '';
const lastLineEmpty = lines[lines.length - 1].trim() === '';
if (content.includes('=>')) { // arrow function
if (lines.length <= 1 || (!firstLineEmpty && !lastLineEmpty)) {
addFormatCodes(content, start, formatBrackets.normal);
}
else {
addFormatCodes(content, start, ['(', ');']);
}
}
else {
if (lines.length <= 1 || (!firstLineEmpty && !lastLineEmpty)) {
addFormatCodes(content, start, formatBrackets.curly);
}
else {
addFormatCodes(content, start, [
firstLineEmpty ? '(' : '(0 +',
lastLineEmpty ? ');' : '+ 0);'
]);
}
}
}
}
function addFormatCodes(code, offset, wrapper) {
const id = 'template_inline_ts_' + i++;
data.set(id, [
wrapper[0],
[
code,
'template',
offset,
codeFeatures,
],
wrapper[1],
]);
}
}
};
exports.default = plugin;
//# sourceMappingURL=vue-template-inline-ts.js.map

View File

@@ -0,0 +1,212 @@
import type { Code, Sfc, VueLanguagePlugin } from '../types';
export declare const tsCodegen: WeakMap<Sfc, {
getScriptRanges: () => {
exportDefault: (import("../types").TextRange & {
expression: import("../types").TextRange;
args: import("../types").TextRange;
argsNode: import("typescript").ObjectLiteralExpression | undefined;
componentsOption: import("../types").TextRange | undefined;
componentsOptionNode: import("typescript").ObjectLiteralExpression | undefined;
directivesOption: import("../types").TextRange | undefined;
nameOption: import("../types").TextRange | undefined;
inheritAttrsOption: string | undefined;
}) | undefined;
classBlockEnd: number | undefined;
bindings: {
range: import("../types").TextRange;
moduleName?: string;
isDefaultImport?: boolean;
isNamespace?: boolean;
}[];
} | undefined;
getScriptSetupRanges: () => {
leadingCommentEndOffset: number;
importSectionEndOffset: number;
bindings: {
range: import("../types").TextRange;
moduleName?: string;
isDefaultImport?: boolean;
isNamespace?: boolean;
}[];
defineProp: {
localName?: import("../types").TextRange;
name?: import("../types").TextRange;
type?: import("../types").TextRange;
modifierType?: import("../types").TextRange;
runtimeType?: import("../types").TextRange;
defaultValue?: import("../types").TextRange;
required?: boolean;
isModel?: boolean;
comments?: import("../types").TextRange;
argNode?: import("typescript").Expression;
}[];
defineProps: ({
callExp: import("../types").TextRange;
exp: import("../types").TextRange;
arg?: import("../types").TextRange;
typeArg?: import("../types").TextRange;
} & {
name?: string;
destructured?: Map<string, import("typescript").Expression | undefined>;
destructuredRest?: string;
statement: import("../types").TextRange;
argNode?: import("typescript").Expression;
}) | undefined;
withDefaults: (Omit<{
callExp: import("../types").TextRange;
exp: import("../types").TextRange;
arg?: import("../types").TextRange;
typeArg?: import("../types").TextRange;
}, "typeArg"> & {
argNode?: import("typescript").Expression;
}) | undefined;
defineEmits: ({
callExp: import("../types").TextRange;
exp: import("../types").TextRange;
arg?: import("../types").TextRange;
typeArg?: import("../types").TextRange;
} & {
name?: string;
hasUnionTypeArg?: boolean;
statement: import("../types").TextRange;
}) | undefined;
defineSlots: ({
callExp: import("../types").TextRange;
exp: import("../types").TextRange;
arg?: import("../types").TextRange;
typeArg?: import("../types").TextRange;
} & {
name?: string;
statement: import("../types").TextRange;
}) | undefined;
defineExpose: {
callExp: import("../types").TextRange;
exp: import("../types").TextRange;
arg?: import("../types").TextRange;
typeArg?: import("../types").TextRange;
} | undefined;
defineOptions: {
name?: string;
inheritAttrs?: string;
} | undefined;
useAttrs: {
callExp: import("../types").TextRange;
exp: import("../types").TextRange;
arg?: import("../types").TextRange;
typeArg?: import("../types").TextRange;
}[];
useCssModule: {
callExp: import("../types").TextRange;
exp: import("../types").TextRange;
arg?: import("../types").TextRange;
typeArg?: import("../types").TextRange;
}[];
useSlots: {
callExp: import("../types").TextRange;
exp: import("../types").TextRange;
arg?: import("../types").TextRange;
typeArg?: import("../types").TextRange;
}[];
useTemplateRef: ({
callExp: import("../types").TextRange;
exp: import("../types").TextRange;
arg?: import("../types").TextRange;
typeArg?: import("../types").TextRange;
} & {
name?: string;
})[];
} | undefined;
getLang: () => string;
getGeneratedScript: () => {
codes: Code[];
generatedTemplate: boolean;
generatedPropsType: boolean;
bypassDefineComponent: boolean;
bindingNames: Set<string>;
localTypes: {
generate: (names: string[]) => Generator<string, void, unknown>;
getUsedNames(): Set<string>;
readonly PrettifyLocal: string;
readonly OmitKeepDiscriminatedUnion: string;
readonly WithDefaults: string;
readonly WithSlots: string;
readonly PropsChildren: string;
readonly TypePropsToOption: string;
readonly OmitIndexSignature: string;
};
inlayHints: import("../codegen/inlayHints").InlayHintInfo[];
};
getGeneratedTemplate: () => {
codes: Code[];
codeFeatures: {
all: import("../types").VueCodeInformation;
none: import("../types").VueCodeInformation;
verification: import("../types").VueCodeInformation;
completion: import("../types").VueCodeInformation;
additionalCompletion: import("../types").VueCodeInformation;
withoutCompletion: import("../types").VueCodeInformation;
navigation: import("../types").VueCodeInformation;
navigationWithoutRename: import("../types").VueCodeInformation;
navigationAndCompletion: import("../types").VueCodeInformation;
navigationAndAdditionalCompletion: import("../types").VueCodeInformation;
navigationAndVerification: import("../types").VueCodeInformation;
withoutNavigation: import("../types").VueCodeInformation;
withoutHighlight: import("../types").VueCodeInformation;
withoutHighlightAndNavigation: import("../types").VueCodeInformation;
withoutHighlightAndCompletion: import("../types").VueCodeInformation;
withoutHighlightAndCompletionAndNavigation: import("../types").VueCodeInformation;
};
resolveCodeFeatures: (features: import("../types").VueCodeInformation) => import("../types").VueCodeInformation;
slots: {
name: string;
offset?: number;
tagRange: [number, number];
nodeLoc: any;
propsVar: string;
}[];
dynamicSlots: {
expVar: string;
propsVar: string;
}[];
dollarVars: Set<string>;
accessExternalVariables: Map<string, Set<number>>;
lastGenericComment: {
content: string;
offset: number;
} | undefined;
blockConditions: string[];
scopedClasses: {
source: string;
className: string;
offset: number;
}[];
emptyClassOffsets: number[];
inlayHints: import("../codegen/inlayHints").InlayHintInfo[];
bindingAttrLocs: import("@vue/compiler-dom").SourceLocation[];
inheritedAttrVars: Set<string>;
templateRefs: Map<string, {
typeExp: string;
offset: number;
}>;
currentComponent: {
ctxVar: string;
used: boolean;
} | undefined;
singleRootElTypes: string[];
singleRootNodes: Set<import("@vue/compiler-dom").ElementNode | null>;
accessExternalVariable(name: string, offset?: number): void;
hasLocalVariable: (name: string) => boolean;
addLocalVariable: (name: string) => void;
removeLocalVariable: (name: string) => void;
getInternalVariable: () => string;
getHoistVariable: (originalVar: string) => string;
generateHoistVariables: () => Generator<string, void, unknown>;
generateConditionGuards: () => Generator<string, void, unknown>;
ignoreError: () => Generator<Code>;
expectError: (prevNode: import("@vue/compiler-dom").CommentNode) => Generator<Code>;
resetDirectiveComments: (endStr: string) => Generator<Code>;
generateAutoImportCompletion: () => Generator<Code>;
} | undefined;
}>;
declare const plugin: VueLanguagePlugin;
export default plugin;

206
node_modules/@vue/language-core/lib/plugins/vue-tsx.js generated vendored Normal file
View File

@@ -0,0 +1,206 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.tsCodegen = void 0;
const shared_1 = require("@vue/shared");
const alien_signals_1 = require("alien-signals");
const path = require("path-browserify");
const script_1 = require("../codegen/script");
const template_1 = require("../codegen/template");
const scriptRanges_1 = require("../parsers/scriptRanges");
const scriptSetupRanges_1 = require("../parsers/scriptSetupRanges");
const vueCompilerOptions_1 = require("../parsers/vueCompilerOptions");
const signals_1 = require("../utils/signals");
const ts_1 = require("../utils/ts");
exports.tsCodegen = new WeakMap();
const fileEditTimes = new Map();
const plugin = ctx => {
let appendedGlobalTypes = false;
return {
version: 2.1,
requiredCompilerOptions: [
'noPropertyAccessFromIndexSignature',
'exactOptionalPropertyTypes',
],
getEmbeddedCodes(fileName, sfc) {
const codegen = useCodegen(fileName, sfc);
const files = [];
if (['js', 'ts', 'jsx', 'tsx'].includes(codegen.getLang())) {
files.push({ id: 'script_' + codegen.getLang(), lang: codegen.getLang() });
}
return files;
},
resolveEmbeddedCode(fileName, sfc, embeddedFile) {
if (/script_(js|jsx|ts|tsx)/.test(embeddedFile.id)) {
const codegen = useCodegen(fileName, sfc);
const tsx = codegen.getGeneratedScript();
if (tsx) {
embeddedFile.content = [...tsx.codes];
}
}
},
};
function useCodegen(fileName, sfc) {
if (!exports.tsCodegen.has(sfc)) {
let appendGlobalTypes = false;
if (!ctx.vueCompilerOptions.__setupedGlobalTypes && !appendedGlobalTypes) {
appendGlobalTypes = true;
appendedGlobalTypes = true;
}
exports.tsCodegen.set(sfc, createTsx(fileName, sfc, ctx, appendGlobalTypes));
}
return exports.tsCodegen.get(sfc);
}
};
exports.default = plugin;
function createTsx(fileName, sfc, ctx, appendGlobalTypes) {
const ts = ctx.modules.typescript;
const getLang = (0, alien_signals_1.computed)(() => {
return !sfc.script && !sfc.scriptSetup ? 'ts'
: sfc.scriptSetup && sfc.scriptSetup.lang !== 'js' ? sfc.scriptSetup.lang
: sfc.script && sfc.script.lang !== 'js' ? sfc.script.lang
: 'js';
});
const getResolvedOptions = (0, alien_signals_1.computed)(() => {
const options = (0, vueCompilerOptions_1.parseVueCompilerOptions)(sfc.comments);
if (options) {
const resolver = new ts_1.CompilerOptionsResolver();
resolver.addConfig(options, path.dirname(fileName));
return resolver.build(ctx.vueCompilerOptions);
}
return ctx.vueCompilerOptions;
});
const getScriptRanges = (0, alien_signals_1.computed)(() => sfc.script
? (0, scriptRanges_1.parseScriptRanges)(ts, sfc.script.ast, !!sfc.scriptSetup, false)
: undefined);
const getScriptSetupRanges = (0, alien_signals_1.computed)(() => sfc.scriptSetup
? (0, scriptSetupRanges_1.parseScriptSetupRanges)(ts, sfc.scriptSetup.ast, getResolvedOptions())
: undefined);
const getSetupBindingNames = (0, signals_1.computedSet)((0, alien_signals_1.computed)(() => {
const newNames = new Set();
const bindings = getScriptSetupRanges()?.bindings;
if (sfc.scriptSetup && bindings) {
for (const { range } of bindings) {
newNames.add(sfc.scriptSetup.content.slice(range.start, range.end));
}
}
return newNames;
}));
const getSetupImportComponentNames = (0, signals_1.computedSet)((0, alien_signals_1.computed)(() => {
const newNames = new Set();
const bindings = getScriptSetupRanges()?.bindings;
if (sfc.scriptSetup && bindings) {
for (const { range, moduleName, isDefaultImport, isNamespace } of bindings) {
if (moduleName
&& isDefaultImport
&& !isNamespace
&& ctx.vueCompilerOptions.extensions.some(ext => moduleName.endsWith(ext))) {
newNames.add(sfc.scriptSetup.content.slice(range.start, range.end));
}
}
}
return newNames;
}));
const getSetupDestructuredPropNames = (0, signals_1.computedSet)((0, alien_signals_1.computed)(() => {
const newNames = new Set(getScriptSetupRanges()?.defineProps?.destructured?.keys());
const rest = getScriptSetupRanges()?.defineProps?.destructuredRest;
if (rest) {
newNames.add(rest);
}
return newNames;
}));
const getSetupTemplateRefNames = (0, signals_1.computedSet)((0, alien_signals_1.computed)(() => {
const newNames = new Set(getScriptSetupRanges()?.useTemplateRef
.map(({ name }) => name)
.filter(name => name !== undefined));
return newNames;
}));
const setupHasDefineSlots = (0, alien_signals_1.computed)(() => !!getScriptSetupRanges()?.defineSlots);
const getSetupSlotsAssignName = (0, alien_signals_1.computed)(() => getScriptSetupRanges()?.defineSlots?.name);
const getSetupPropsAssignName = (0, alien_signals_1.computed)(() => getScriptSetupRanges()?.defineProps?.name);
const getSetupInheritAttrs = (0, alien_signals_1.computed)(() => {
const value = getScriptSetupRanges()?.defineOptions?.inheritAttrs ?? getScriptRanges()?.exportDefault?.inheritAttrsOption;
return value !== 'false';
});
const getComponentSelfName = (0, alien_signals_1.computed)(() => {
const { exportDefault } = getScriptRanges() ?? {};
if (sfc.script && exportDefault?.nameOption) {
const { nameOption } = exportDefault;
return sfc.script.content.slice(nameOption.start + 1, nameOption.end - 1);
}
const { defineOptions } = getScriptSetupRanges() ?? {};
if (sfc.scriptSetup && defineOptions?.name) {
return defineOptions.name;
}
const baseName = path.basename(fileName);
return (0, shared_1.capitalize)((0, shared_1.camelize)(baseName.slice(0, baseName.lastIndexOf('.'))));
});
const getGeneratedTemplate = (0, alien_signals_1.computed)(() => {
if (getResolvedOptions().skipTemplateCodegen || !sfc.template) {
return;
}
const codes = [];
const codegen = (0, template_1.generateTemplate)({
ts,
compilerOptions: ctx.compilerOptions,
vueCompilerOptions: getResolvedOptions(),
template: sfc.template,
edited: getResolvedOptions().__test || (fileEditTimes.get(fileName) ?? 0) >= 2,
scriptSetupBindingNames: getSetupBindingNames(),
scriptSetupImportComponentNames: getSetupImportComponentNames(),
destructuredPropNames: getSetupDestructuredPropNames(),
templateRefNames: getSetupTemplateRefNames(),
hasDefineSlots: setupHasDefineSlots(),
slotsAssignName: getSetupSlotsAssignName(),
propsAssignName: getSetupPropsAssignName(),
inheritAttrs: getSetupInheritAttrs(),
selfComponentName: getComponentSelfName(),
});
let current = codegen.next();
while (!current.done) {
const code = current.value;
codes.push(code);
current = codegen.next();
}
return {
...current.value,
codes,
};
});
const getGeneratedScript = (0, alien_signals_1.computed)(() => {
const codes = [];
const codegen = (0, script_1.generateScript)({
ts,
compilerOptions: ctx.compilerOptions,
vueCompilerOptions: getResolvedOptions(),
sfc: sfc,
edited: getResolvedOptions().__test || (fileEditTimes.get(fileName) ?? 0) >= 2,
fileName,
lang: getLang(),
scriptRanges: getScriptRanges(),
scriptSetupRanges: getScriptSetupRanges(),
templateCodegen: getGeneratedTemplate(),
destructuredPropNames: getSetupDestructuredPropNames(),
templateRefNames: getSetupTemplateRefNames(),
appendGlobalTypes,
});
fileEditTimes.set(fileName, (fileEditTimes.get(fileName) ?? 0) + 1);
let current = codegen.next();
while (!current.done) {
const code = current.value;
codes.push(code);
current = codegen.next();
}
return {
...current.value,
codes,
};
});
return {
getScriptRanges,
getScriptSetupRanges,
getLang,
getGeneratedScript,
getGeneratedTemplate,
};
}
//# sourceMappingURL=vue-tsx.js.map