2021-03-20 05:56:08 +01:00
|
|
|
const fs = require("fs");
|
2021-03-20 11:42:29 +01:00
|
|
|
const worker = require("@bazel/worker");
|
|
|
|
const svelte2tsx = require("svelte2tsx");
|
|
|
|
const preprocess = require("svelte-preprocess");
|
2021-03-20 14:13:27 +01:00
|
|
|
import * as ts from "typescript";
|
|
|
|
import * as svelte from "svelte/compiler";
|
2021-03-20 05:56:08 +01:00
|
|
|
|
2021-03-20 14:49:10 +01:00
|
|
|
let parsedCommandLine: ts.ParsedCommandLine = {
|
|
|
|
fileNames: [],
|
|
|
|
errors: [],
|
|
|
|
options: {
|
|
|
|
jsx: ts.JsxEmit.Preserve,
|
|
|
|
declaration: true,
|
|
|
|
emitDeclarationOnly: true,
|
|
|
|
skipLibCheck: true,
|
|
|
|
},
|
2021-03-20 11:42:29 +01:00
|
|
|
};
|
2021-03-20 14:49:10 +01:00
|
|
|
|
|
|
|
// largely taken from https://github.com/Asana/bazeltsc/blob/7dfa0ba2bd5eb9ee556e146df35cf793fad2d2c3/src/bazeltsc.ts (MIT)
|
|
|
|
const languageServiceHost: ts.LanguageServiceHost = {
|
|
|
|
getCompilationSettings: (): ts.CompilerOptions => parsedCommandLine.options,
|
|
|
|
getNewLine: () => ts.sys.newLine,
|
|
|
|
getScriptFileNames: (): string[] => parsedCommandLine.fileNames,
|
|
|
|
getScriptVersion: (fileName: string): string => {
|
|
|
|
// If the file's size or modified-timestamp changed, it's a different version.
|
|
|
|
return (
|
|
|
|
ts.sys.getFileSize!(fileName) +
|
|
|
|
":" +
|
|
|
|
ts.sys.getModifiedTime!(fileName)!.getTime()
|
|
|
|
);
|
|
|
|
},
|
|
|
|
getScriptSnapshot: (fileName: string): ts.IScriptSnapshot | undefined => {
|
|
|
|
if (!ts.sys.fileExists(fileName)) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
let text = ts.sys.readFile(fileName)!;
|
|
|
|
return {
|
|
|
|
getText: (start: number, end: number) => {
|
|
|
|
if (start === 0 && end === text.length) {
|
|
|
|
// optimization
|
|
|
|
return text;
|
|
|
|
} else {
|
|
|
|
return text.slice(start, end);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
getLength: () => text.length,
|
|
|
|
getChangeRange: (
|
|
|
|
oldSnapshot: ts.IScriptSnapshot
|
|
|
|
): ts.TextChangeRange | undefined => {
|
|
|
|
const oldText = oldSnapshot.getText(0, oldSnapshot.getLength());
|
|
|
|
|
|
|
|
// Find the offset of the first char that differs between oldText and text
|
|
|
|
let firstDiff = 0;
|
|
|
|
while (
|
|
|
|
firstDiff < oldText.length &&
|
|
|
|
firstDiff < text.length &&
|
|
|
|
text[firstDiff] === oldText[firstDiff]
|
|
|
|
) {
|
|
|
|
firstDiff++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the offset of the last char that differs between oldText and text
|
|
|
|
let oldIndex = oldText.length;
|
|
|
|
let newIndex = text.length;
|
|
|
|
while (
|
|
|
|
oldIndex > firstDiff &&
|
|
|
|
newIndex > firstDiff &&
|
|
|
|
oldText[oldIndex - 1] === text[newIndex - 1]
|
|
|
|
) {
|
|
|
|
oldIndex--;
|
|
|
|
newIndex--;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
span: {
|
|
|
|
start: firstDiff,
|
|
|
|
length: oldIndex - firstDiff,
|
|
|
|
},
|
|
|
|
newLength: newIndex - firstDiff,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
dispose: (): void => {
|
|
|
|
text = "";
|
|
|
|
},
|
|
|
|
};
|
|
|
|
},
|
|
|
|
getCurrentDirectory: ts.sys.getCurrentDirectory,
|
|
|
|
getDefaultLibFileName: ts.getDefaultLibFilePath,
|
|
|
|
};
|
|
|
|
|
|
|
|
const languageService = ts.createLanguageService(languageServiceHost);
|
|
|
|
|
|
|
|
function compile(tsPath, shims) {
|
|
|
|
parsedCommandLine.fileNames = [tsPath, ...shims];
|
|
|
|
const program = languageService.getProgram()!;
|
|
|
|
const tsHost = ts.createCompilerHost(parsedCommandLine.options);
|
|
|
|
const createdFiles = {};
|
|
|
|
tsHost.writeFile = (fileName, contents) => (createdFiles[fileName] = contents);
|
|
|
|
program.emit(undefined /* all files */, tsHost.writeFile);
|
|
|
|
return createdFiles[parsedCommandLine.fileNames[0].replace(".ts", ".d.ts")];
|
|
|
|
}
|
2021-03-20 05:56:08 +01:00
|
|
|
|
2021-03-20 11:42:29 +01:00
|
|
|
function writeFile(file, data) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
fs.writeFile(file, data, (err) => {
|
|
|
|
if (err) {
|
|
|
|
reject(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
resolve();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2021-03-20 05:56:08 +01:00
|
|
|
|
2021-03-20 11:42:29 +01:00
|
|
|
function readFile(file) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
fs.readFile(file, "utf8", (err, data) => {
|
|
|
|
if (err) {
|
|
|
|
reject(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
resolve(data);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2021-03-20 05:56:08 +01:00
|
|
|
|
2021-03-20 11:42:29 +01:00
|
|
|
async function writeDts(tsPath, dtsPath, shims) {
|
2021-03-20 14:49:10 +01:00
|
|
|
const dtsSource = compile(tsPath, shims);
|
2021-03-20 11:42:29 +01:00
|
|
|
await writeFile(dtsPath, dtsSource);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function writeTs(svelteSource, sveltePath, tsPath) {
|
|
|
|
let tsSource = svelte2tsx(svelteSource, {
|
|
|
|
filename: sveltePath,
|
|
|
|
strictMode: true,
|
|
|
|
isTsFile: true,
|
|
|
|
});
|
|
|
|
let codeLines = tsSource.code.split("\n");
|
|
|
|
// replace the "///<reference types="svelte" />" with a line
|
|
|
|
// turning off checking, as we'll use svelte-check for that
|
|
|
|
codeLines[0] = "// @ts-nocheck";
|
|
|
|
await writeFile(tsPath, codeLines.join("\n"));
|
|
|
|
}
|
|
|
|
|
|
|
|
async function writeJs(source, inputFilename, outputPath) {
|
|
|
|
const preprocessOptions = preprocess({});
|
|
|
|
preprocessOptions.filename = inputFilename;
|
2021-03-20 05:56:08 +01:00
|
|
|
|
|
|
|
try {
|
2021-03-20 11:42:29 +01:00
|
|
|
const processed = await svelte.preprocess(source, preprocessOptions);
|
2021-03-20 14:13:27 +01:00
|
|
|
const result = svelte.compile(processed.toString!(), {
|
2021-03-20 11:42:29 +01:00
|
|
|
format: "esm",
|
|
|
|
generate: "dom",
|
|
|
|
filename: outputPath,
|
|
|
|
});
|
|
|
|
// warnings are an error
|
|
|
|
if (result.warnings.length > 0) {
|
|
|
|
console.log(`warnings during compile: ${result.warnings}`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const outputSource = result.js.code;
|
|
|
|
await writeFile(outputPath, outputSource);
|
2021-03-20 05:56:08 +01:00
|
|
|
} catch (err) {
|
2021-03-20 11:42:29 +01:00
|
|
|
console.log(`compile failed: ${err}`);
|
|
|
|
return;
|
2021-03-20 05:56:08 +01:00
|
|
|
}
|
2021-03-20 11:42:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
async function compileSvelte(args) {
|
|
|
|
const [sveltePath, mjsPath, dtsPath, tempTsPath, ...shims] = args;
|
|
|
|
const svelteSource = await readFile(sveltePath);
|
|
|
|
|
|
|
|
await writeTs(svelteSource, sveltePath, tempTsPath);
|
|
|
|
await writeDts(tempTsPath, dtsPath, shims);
|
|
|
|
await writeJs(svelteSource, sveltePath, mjsPath);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function main(args) {
|
|
|
|
if (worker.runAsWorker(process.argv)) {
|
|
|
|
worker.log("Svelte running as a Bazel worker");
|
|
|
|
worker.runWorkerLoop(compileSvelte);
|
|
|
|
} else {
|
|
|
|
const paramFile = process.argv[2].replace(/^@/, "");
|
|
|
|
const commandLineArgs = fs.readFileSync(paramFile, "utf-8").trim().split("\n");
|
|
|
|
console.log("Svelte running as a standalone process");
|
|
|
|
compileSvelte(commandLineArgs);
|
2021-03-20 05:56:08 +01:00
|
|
|
}
|
2021-03-20 11:42:29 +01:00
|
|
|
}
|
2021-03-20 05:56:08 +01:00
|
|
|
|
2021-03-20 11:42:29 +01:00
|
|
|
if (require.main === module) {
|
|
|
|
main(process.argv.slice(2));
|
|
|
|
process.exitCode = 0;
|
|
|
|
}
|