UNPKG

@vue.ts/language

Version:
290 lines (284 loc) 9 kB
'use strict'; const node_module = require('node:module'); const path = require('node:path'); const typescript = require('@volar/typescript'); const vue = require('@vue/language-core'); const common = require('@vue.ts/common'); var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null; function _interopNamespaceCompat(e) { if (e && typeof e === 'object' && 'default' in e) return e; const n = Object.create(null); if (e) { for (const k in e) { n[k] = e[k]; } } n.default = e; return n; } const path__namespace = /*#__PURE__*/_interopNamespaceCompat(path); const vue__namespace = /*#__PURE__*/_interopNamespaceCompat(vue); const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))); function createLanguage(tsconfigPath, ts = require$1("typescript")) { const tsconfig = common.normalizePath(tsconfigPath); return createLanguageWorker( () => vue__namespace.createParsedCommandLine(ts, ts.sys, tsconfigPath), path__namespace.dirname(tsconfig), tsconfig, ts ); } function createLanguageWorker(loadParsedCommandLine, rootPath, configFileName, ts) { let parsedCommandLine = loadParsedCommandLine(); let fileNames = parsedCommandLine.fileNames.map(common.normalizePath); let projectVersion = 0; const scriptSnapshots = /* @__PURE__ */ new Map(); const _host = { // Commented to wait for volar 2.2.0-alpha.0 // ...ts.sys, // configFileName, getCurrentDirectory: () => rootPath, getProjectVersion: () => projectVersion.toString(), getCompilationSettings: () => parsedCommandLine.options, getScriptFileNames: () => fileNames, getProjectReferences: () => parsedCommandLine.projectReferences, getScriptSnapshot: (fileName) => { if (!scriptSnapshots.has(fileName)) { const fileText = ts.sys.readFile(fileName); if (fileText !== void 0) { scriptSnapshots.set(fileName, ts.ScriptSnapshot.fromString(fileText)); } } return scriptSnapshots.get(fileName); }, getLanguageId: (fileName) => { if (parsedCommandLine.vueOptions.extensions.some( (ext) => fileName.endsWith(ext) )) { return "vue"; } return vue__namespace.resolveCommonLanguageId(fileName); } // scriptIdToFileName: (id) => id, // fileNameToScriptId: (id) => id, }; return { ...baseCreateLanguageWorker( ts, configFileName, _host, parsedCommandLine.vueOptions ), updateFile(fileName, text) { fileName = common.normalizePath(fileName); scriptSnapshots.set(fileName, ts.ScriptSnapshot.fromString(text)); projectVersion++; }, deleteFile(fileName) { fileName = common.normalizePath(fileName); fileNames = fileNames.filter((f) => f !== fileName); projectVersion++; }, reload() { parsedCommandLine = loadParsedCommandLine(); fileNames = parsedCommandLine.fileNames.map(common.normalizePath); this.clearCache(); }, clearCache() { scriptSnapshots.clear(); projectVersion++; } }; } function baseCreateLanguageWorker(ts, configFileName, host, vueCompilerOptions) { const vueLanguagePlugin = vue__namespace.createVueLanguagePlugin( ts, (id) => id, (fileName) => { if (ts.sys.useCaseSensitiveFileNames) { return host.getScriptFileNames().includes(fileName) ?? false; } else { const lowerFileName = fileName.toLowerCase(); for (const rootFile of host.getScriptFileNames()) { if (rootFile.toLowerCase() === lowerFileName) { return true; } } return false; } }, host.getCompilationSettings(), vueCompilerOptions ); const language = typescript.createLanguage( ts, ts.sys, [vueLanguagePlugin], configFileName, host, { fileIdToFileName: (id) => id, fileNameToFileId: (id) => id } ); const { languageServiceHost } = language.typescript; const tsLs = ts.createLanguageService(languageServiceHost); const getScriptKind = languageServiceHost.getScriptKind?.bind(languageServiceHost); languageServiceHost.getScriptKind = (fileName) => { if (fileName.endsWith(".vue.js")) { return ts.ScriptKind.TS; } if (fileName.endsWith(".vue.jsx")) { return ts.ScriptKind.TSX; } return getScriptKind(fileName); }; const program = tsLs.getProgram(); const typeChecker = program.getTypeChecker(); function getScriptSetupBlock(normalizedFilepath) { const sourceFile = language.files.get(normalizedFilepath)?.generated?.code; if (!(sourceFile instanceof vue__namespace.VueGeneratedCode)) { return; } if (!sourceFile.sfc.scriptSetup) { return; } const { lang } = sourceFile.sfc.scriptSetup; if (lang !== "ts" && lang !== "tsx") { return; } return sourceFile.sfc.scriptSetup; } function findNodeByRange(filepath, filter) { filepath = common.normalizePath(filepath); const scriptSetupBlock = getScriptSetupBlock(filepath); if (!scriptSetupBlock) { return; } const { startTagEnd: offset } = scriptSetupBlock; const scriptSetupAst = getScriptSetupAst(filepath); if (!scriptSetupAst) { return; } const scriptSetupRanges = vue__namespace.parseScriptSetupRanges( ts, scriptSetupAst, vueCompilerOptions ); const virtualFileAst = getVirtualFileOrTsAst(filepath); if (!virtualFileAst) { return; } const virtualTsFileScriptSetupRanges = vue__namespace.parseScriptSetupRanges( ts, virtualFileAst, vueCompilerOptions ); const sourceRange = filter(scriptSetupRanges); const virtualTsRange = filter(virtualTsFileScriptSetupRanges); if (!sourceRange || !virtualTsRange) { return; } let foundVirtualFileNode; virtualFileAst.forEachChild(function traverse(node) { if (node.getStart(virtualFileAst) === virtualTsRange.start && node.getEnd() === virtualTsRange.end) { foundVirtualFileNode = node; } else { node.forEachChild(traverse); } }); let foundSetupNode; scriptSetupAst.forEachChild(function traverse(node) { if (node.getStart(scriptSetupAst) === sourceRange.start && node.getEnd() === sourceRange.end) { foundSetupNode = node; } else { node.forEachChild(traverse); } }); const setupRange = { start: offset + sourceRange.start, end: offset + sourceRange.end }; return { virtualFileNode: foundVirtualFileNode, scriptNode: foundSetupNode, setupRange, offset }; } function getScriptSetupAst(filepath) { filepath = common.normalizePath(filepath); const scriptSetupBlock = getScriptSetupBlock(filepath); if (!scriptSetupBlock) { return; } return scriptSetupBlock.ast; } function getVirtualFileOrTsAst(filepath) { filepath = common.normalizePath(filepath); let virtualFileOrTsAst = program.getSourceFile(filepath); if (virtualFileOrTsAst) { return virtualFileOrTsAst; } const scriptSetupBlock = getScriptSetupBlock(filepath); if (!scriptSetupBlock) { return; } const { lang } = scriptSetupBlock; virtualFileOrTsAst = program.getSourceFile(`${filepath}.${lang}`); return virtualFileOrTsAst; } function traverseAst(filepath, cb) { filepath = common.normalizePath(filepath); const scriptSetupAst = getScriptSetupAst(filepath); const virtualFileOrTsAst = getVirtualFileOrTsAst(filepath); if (!virtualFileOrTsAst) { return; } if (scriptSetupAst) { scriptSetupAst.forEachChild(function traverse(node) { cb(node, { scriptSetupAst, virtualFileOrTsAst, isVirtualOrTsFile: false }); node.forEachChild(traverse); }); } if (virtualFileOrTsAst) { virtualFileOrTsAst.forEachChild(function traverse(node) { cb(node, { scriptSetupAst, virtualFileOrTsAst, isVirtualOrTsFile: true }); node.forEachChild(traverse); }); } } return { findNodeByRange, getScriptSetupAst, getVirtualFileAst: getVirtualFileOrTsAst, traverseAst, __internal__: { tsLs, program, typeChecker } }; } const LANGUAGE_GLOBAL_KEY = "__VUETS_LANGUAGE__"; function ensureLanguage(tsconfigPath) { if (!globalThis[LANGUAGE_GLOBAL_KEY]) { globalThis[LANGUAGE_GLOBAL_KEY] = createLanguage(tsconfigPath); } } function getLanguage() { if (!globalThis[LANGUAGE_GLOBAL_KEY]) { throw new Error("[@vue.ts/language] Language not created!"); } return globalThis[LANGUAGE_GLOBAL_KEY]; } exports.ensureLanguage = ensureLanguage; exports.getLanguage = getLanguage;