mirror of
https://github.com/Sevichecc/Urara-Blog.git
synced 2025-05-06 06:59:13 +08:00
321 lines
13 KiB
Text
321 lines
13 KiB
Text
"use strict";
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.transformer = exports.loadTsconfig = void 0;
|
|
const path_1 = require("path");
|
|
const typescript_1 = __importDefault(require("typescript"));
|
|
const compiler_1 = require("svelte/compiler");
|
|
const magic_string_1 = __importDefault(require("magic-string"));
|
|
const sorcery_1 = __importDefault(require("sorcery"));
|
|
const errors_1 = require("../modules/errors");
|
|
const markup_1 = require("../modules/markup");
|
|
const utils_1 = require("../modules/utils");
|
|
const package_json_1 = __importDefault(require("svelte/package.json"));
|
|
const injectedCodeSeparator = 'const $$$$$$$$ = null;';
|
|
/**
|
|
* Map of valid tsconfigs (no errors). Key is the path.
|
|
*/
|
|
const tsconfigMap = new Map();
|
|
function createFormatDiagnosticsHost(cwd) {
|
|
return {
|
|
getCanonicalFileName: (fileName) => fileName.replace('.injected.ts', ''),
|
|
getCurrentDirectory: () => cwd,
|
|
getNewLine: () => typescript_1.default.sys.newLine,
|
|
};
|
|
}
|
|
function formatDiagnostics(diagnostics, basePath) {
|
|
if (Array.isArray(diagnostics)) {
|
|
return typescript_1.default.formatDiagnosticsWithColorAndContext(diagnostics, createFormatDiagnosticsHost(basePath));
|
|
}
|
|
return typescript_1.default.formatDiagnostic(diagnostics, createFormatDiagnosticsHost(basePath));
|
|
}
|
|
const importTransformer = (context) => {
|
|
const visit = (node) => {
|
|
var _a;
|
|
if (typescript_1.default.isImportDeclaration(node)) {
|
|
if ((_a = node.importClause) === null || _a === void 0 ? void 0 : _a.isTypeOnly) {
|
|
return typescript_1.default.createEmptyStatement();
|
|
}
|
|
return typescript_1.default.createImportDeclaration(node.decorators, node.modifiers, node.importClause, node.moduleSpecifier);
|
|
}
|
|
return typescript_1.default.visitEachChild(node, (child) => visit(child), context);
|
|
};
|
|
return (node) => typescript_1.default.visitNode(node, visit);
|
|
};
|
|
function getScriptContent(markup, module) {
|
|
const regex = (0, markup_1.createTagRegex)('script', 'gi');
|
|
let match;
|
|
while ((match = regex.exec(markup)) !== null) {
|
|
const { context } = (0, markup_1.parseAttributes)(match[1] || '');
|
|
if ((context !== 'module' && !module) || (context === 'module' && module)) {
|
|
return match[2];
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
function createSourceMapChain({ filename, content, compilerOptions, }) {
|
|
if (compilerOptions.sourceMap) {
|
|
return {
|
|
content: {
|
|
[filename]: content,
|
|
},
|
|
sourcemaps: {},
|
|
};
|
|
}
|
|
}
|
|
function injectVarsToCode({ content, markup, filename, attributes, sourceMapChain, }) {
|
|
if (!markup)
|
|
return content;
|
|
const { vars } = (0, compiler_1.compile)((0, markup_1.stripTags)(markup), {
|
|
generate: false,
|
|
varsReport: 'full',
|
|
errorMode: 'warn',
|
|
filename,
|
|
});
|
|
const sep = `\n${injectedCodeSeparator}\n`;
|
|
const varnames = vars.map((v) => v.name.startsWith('$') && !v.name.startsWith('$$')
|
|
? `${v.name},${v.name.slice(1)}`
|
|
: v.name);
|
|
const contentForCodestores = content +
|
|
// Append instance script content because it's valid
|
|
// to import a store in module script and autosubscribe to it in instance script
|
|
((attributes === null || attributes === void 0 ? void 0 : attributes.context) === 'module' ? getScriptContent(markup, false) : '');
|
|
// This regex extracts all possible store variables
|
|
// TODO investigate if it's possible to achieve this with a
|
|
// TS transformer (previous attemps have failed)
|
|
const codestores = Array.from(contentForCodestores.match(/\$[^\s();:,[\]{}.?!+\-=*/\\~|&%<>^`"'°§#0-9][^\s();:,[\]{}.?!+\-=*/\\~|&%<>^`"'°§#]*/g) || [], (name) => name.slice(1)).filter((name) => !utils_1.JAVASCRIPT_RESERVED_KEYWORD_SET.has(name));
|
|
const varsString = [...codestores, ...varnames].join(',');
|
|
const injectedVars = `const $$vars$$ = [${varsString}];`;
|
|
// Append instance/markup script content because it's valid
|
|
// to import things in one and reference it in the other.
|
|
const injectedCode = (attributes === null || attributes === void 0 ? void 0 : attributes.context) === 'module'
|
|
? `${sep}${getScriptContent(markup, false)}\n${injectedVars}`
|
|
: `${sep}${getScriptContent(markup, true)}\n${injectedVars}`;
|
|
if (sourceMapChain) {
|
|
const ms = new magic_string_1.default(content).append(injectedCode);
|
|
const fname = `${filename}.injected.ts`;
|
|
const code = ms.toString();
|
|
const map = ms.generateMap({
|
|
source: filename,
|
|
file: fname,
|
|
hires: true,
|
|
});
|
|
sourceMapChain.content[fname] = code;
|
|
sourceMapChain.sourcemaps[fname] = map;
|
|
return code;
|
|
}
|
|
return `${content}${injectedCode}`;
|
|
}
|
|
function stripInjectedCode({ transpiledCode, markup, filename, sourceMapChain, }) {
|
|
if (!markup)
|
|
return transpiledCode;
|
|
const injectedCodeStart = transpiledCode.indexOf(injectedCodeSeparator);
|
|
if (sourceMapChain) {
|
|
const ms = new magic_string_1.default(transpiledCode).snip(0, injectedCodeStart);
|
|
const source = `${filename}.transpiled.js`;
|
|
const file = `${filename}.js`;
|
|
const code = ms.toString();
|
|
const map = ms.generateMap({
|
|
source,
|
|
file,
|
|
hires: true,
|
|
});
|
|
sourceMapChain.content[file] = code;
|
|
sourceMapChain.sourcemaps[file] = map;
|
|
return code;
|
|
}
|
|
return transpiledCode.slice(0, injectedCodeStart);
|
|
}
|
|
async function concatSourceMaps({ filename, markup, sourceMapChain, }) {
|
|
if (!sourceMapChain)
|
|
return;
|
|
if (!markup) {
|
|
return sourceMapChain.sourcemaps[`${filename}.js`];
|
|
}
|
|
const chain = await sorcery_1.default.load(`${filename}.js`, sourceMapChain);
|
|
return chain.apply();
|
|
}
|
|
function getCompilerOptions({ filename, options, basePath, }) {
|
|
var _a;
|
|
const inputOptions = (_a = options.compilerOptions) !== null && _a !== void 0 ? _a : {};
|
|
const { errors, options: convertedCompilerOptions } = options.tsconfigFile !== false || options.tsconfigDirectory
|
|
? loadTsconfig(inputOptions, filename, options)
|
|
: typescript_1.default.convertCompilerOptionsFromJson(inputOptions, basePath);
|
|
if (errors.length) {
|
|
throw new Error(formatDiagnostics(errors, basePath));
|
|
}
|
|
const compilerOptions = {
|
|
target: typescript_1.default.ScriptTarget.ES2015,
|
|
moduleResolution: typescript_1.default.ModuleResolutionKind.NodeJs,
|
|
...convertedCompilerOptions,
|
|
importsNotUsedAsValues: typescript_1.default.ImportsNotUsedAsValues.Error,
|
|
allowNonTsExtensions: true,
|
|
// Clear outDir since it causes source map issues when the files aren't actually written to disk.
|
|
outDir: undefined,
|
|
};
|
|
if (compilerOptions.target === typescript_1.default.ScriptTarget.ES3 ||
|
|
compilerOptions.target === typescript_1.default.ScriptTarget.ES5) {
|
|
throw new Error(`Svelte only supports es6+ syntax. Set your 'compilerOptions.target' to 'es6' or higher.`);
|
|
}
|
|
return compilerOptions;
|
|
}
|
|
function transpileTs({ code, fileName, basePath, options, compilerOptions, transformers, }) {
|
|
const { outputText: transpiledCode, sourceMapText, diagnostics, } = typescript_1.default.transpileModule(code, {
|
|
fileName,
|
|
compilerOptions,
|
|
reportDiagnostics: options.reportDiagnostics !== false,
|
|
transformers,
|
|
});
|
|
if (diagnostics && diagnostics.length > 0) {
|
|
// could this be handled elsewhere?
|
|
const hasError = diagnostics.some((d) => d.category === typescript_1.default.DiagnosticCategory.Error);
|
|
const formattedDiagnostics = formatDiagnostics(diagnostics, basePath);
|
|
console.log(formattedDiagnostics);
|
|
if (hasError) {
|
|
(0, errors_1.throwTypescriptError)();
|
|
}
|
|
}
|
|
return { transpiledCode, sourceMapText, diagnostics };
|
|
}
|
|
function loadTsconfig(compilerOptionsJSON, filename, tsOptions) {
|
|
if (typeof tsOptions.tsconfigFile === 'boolean') {
|
|
return { errors: [], options: compilerOptionsJSON };
|
|
}
|
|
let basePath = process.cwd();
|
|
const fileDirectory = (tsOptions.tsconfigDirectory ||
|
|
(0, path_1.dirname)(filename));
|
|
let tsconfigFile = tsOptions.tsconfigFile ||
|
|
typescript_1.default.findConfigFile(fileDirectory, typescript_1.default.sys.fileExists);
|
|
if (!tsconfigFile) {
|
|
return { errors: [], options: compilerOptionsJSON };
|
|
}
|
|
tsconfigFile = (0, path_1.isAbsolute)(tsconfigFile)
|
|
? tsconfigFile
|
|
: (0, path_1.join)(basePath, tsconfigFile);
|
|
basePath = (0, path_1.dirname)(tsconfigFile);
|
|
if (tsconfigMap.has(tsconfigFile)) {
|
|
return {
|
|
errors: [],
|
|
options: tsconfigMap.get(tsconfigFile),
|
|
};
|
|
}
|
|
const { error, config } = typescript_1.default.readConfigFile(tsconfigFile, typescript_1.default.sys.readFile);
|
|
if (error) {
|
|
throw new Error(formatDiagnostics(error, basePath));
|
|
}
|
|
// Do this so TS will not search for initial files which might take a while
|
|
config.include = [];
|
|
let { errors, options } = typescript_1.default.parseJsonConfigFileContent(config, typescript_1.default.sys, basePath, compilerOptionsJSON, tsconfigFile);
|
|
// Filter out "no files found error"
|
|
errors = errors.filter((d) => d.code !== 18003);
|
|
if (errors.length === 0) {
|
|
tsconfigMap.set(tsconfigFile, options);
|
|
}
|
|
return { errors, options };
|
|
}
|
|
exports.loadTsconfig = loadTsconfig;
|
|
async function mixedImportsTranspiler({ content, filename = 'source.svelte', markup, options = {}, attributes, compilerOptions, basePath, }) {
|
|
const sourceMapChain = createSourceMapChain({
|
|
filename,
|
|
content,
|
|
compilerOptions,
|
|
});
|
|
const injectedCode = injectVarsToCode({
|
|
content,
|
|
markup,
|
|
filename,
|
|
attributes,
|
|
sourceMapChain,
|
|
});
|
|
const { transpiledCode, sourceMapText, diagnostics } = transpileTs({
|
|
code: injectedCode,
|
|
fileName: `${filename}.injected.ts`,
|
|
basePath,
|
|
options,
|
|
compilerOptions,
|
|
});
|
|
if (sourceMapChain && sourceMapText) {
|
|
const fname = `${filename}.transpiled.js`;
|
|
sourceMapChain.content[fname] = transpiledCode;
|
|
sourceMapChain.sourcemaps[fname] = JSON.parse(sourceMapText);
|
|
}
|
|
const code = stripInjectedCode({
|
|
transpiledCode,
|
|
markup,
|
|
filename,
|
|
sourceMapChain,
|
|
});
|
|
// Sorcery tries to load the code/map from disk if it's empty,
|
|
// prevent that because it would try to load inexistent files
|
|
// https://github.com/Rich-Harris/sorcery/issues/167
|
|
if (!code) {
|
|
return { code, diagnostics };
|
|
}
|
|
const map = await concatSourceMaps({
|
|
filename,
|
|
markup,
|
|
sourceMapChain,
|
|
});
|
|
return {
|
|
code,
|
|
map,
|
|
diagnostics,
|
|
};
|
|
}
|
|
async function simpleTranspiler({ content, filename = 'source.svelte', options = {}, compilerOptions, basePath, }) {
|
|
const { transpiledCode, sourceMapText, diagnostics } = transpileTs({
|
|
code: content,
|
|
// `preserveValueImports` essentially does the same as our import transformer,
|
|
// keeping all imports that are not type imports
|
|
transformers: compilerOptions.preserveValueImports
|
|
? undefined
|
|
: { before: [importTransformer] },
|
|
fileName: filename,
|
|
basePath,
|
|
options,
|
|
compilerOptions,
|
|
});
|
|
return {
|
|
code: transpiledCode,
|
|
map: sourceMapText,
|
|
diagnostics,
|
|
};
|
|
}
|
|
const transformer = async ({ content, filename, markup, options = {}, attributes, }) => {
|
|
const basePath = process.cwd();
|
|
if (filename == null)
|
|
return { code: content };
|
|
filename = (0, path_1.isAbsolute)(filename) ? filename : (0, path_1.resolve)(basePath, filename);
|
|
const compilerOptions = getCompilerOptions({ filename, options, basePath });
|
|
const versionParts = package_json_1.default.version.split('.');
|
|
const canUseMixedImportsTranspiler = +versionParts[0] > 3 || (+versionParts[0] === 3 && +versionParts[1] >= 39);
|
|
if (!canUseMixedImportsTranspiler && options.handleMixedImports) {
|
|
throw new Error('You need at least Svelte 3.39 to use the handleMixedImports option');
|
|
}
|
|
const handleMixedImports = !compilerOptions.preserveValueImports &&
|
|
(options.handleMixedImports === false
|
|
? false
|
|
: options.handleMixedImports || canUseMixedImportsTranspiler);
|
|
return handleMixedImports
|
|
? mixedImportsTranspiler({
|
|
content,
|
|
filename,
|
|
markup,
|
|
options,
|
|
attributes,
|
|
compilerOptions,
|
|
basePath,
|
|
})
|
|
: simpleTranspiler({
|
|
content,
|
|
filename,
|
|
markup,
|
|
options,
|
|
attributes,
|
|
compilerOptions,
|
|
basePath,
|
|
});
|
|
};
|
|
exports.transformer = transformer;
|