svelte-language-server
Version:
A language server for Svelte
318 lines • 14.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SvelteFragmentMapper = exports.FallbackTranspiledSvelteDocument = exports.TranspiledSvelteDocument = exports.SvelteDocument = exports.TranspileErrorSource = void 0;
const trace_mapping_1 = require("@jridgewell/trace-mapping");
const documents_1 = require("../../lib/documents");
const utils_1 = require("../../utils");
var TranspileErrorSource;
(function (TranspileErrorSource) {
TranspileErrorSource["Script"] = "Script";
TranspileErrorSource["Style"] = "Style";
})(TranspileErrorSource || (exports.TranspileErrorSource = TranspileErrorSource = {}));
/**
* Represents a text document that contains a svelte component.
*/
class SvelteDocument {
get config() {
return this.parent.configPromise;
}
constructor(parent) {
this.parent = parent;
this.languageId = 'svelte';
this.version = 0;
this.uri = this.parent.uri;
this.script = this.parent.scriptInfo;
this.moduleScript = this.parent.moduleScriptInfo;
this.style = this.parent.styleInfo;
this.version = this.parent.version;
}
getText() {
return this.parent.getText();
}
getFilePath() {
return this.parent.getFilePath() || '';
}
offsetAt(position) {
return this.parent.offsetAt(position);
}
async getTranspiled() {
if (!this.transpiledDoc) {
const [major, minor] = this.parent.getSvelteVersion();
if (major > 3 || (major === 3 && minor >= 32)) {
this.transpiledDoc = TranspiledSvelteDocument.create(this.parent, await this.config);
}
else {
this.transpiledDoc = FallbackTranspiledSvelteDocument.create(this.parent, (await this.config)?.preprocess);
}
}
return this.transpiledDoc;
}
async getCompiled() {
if (!this.compileResult) {
this.compileResult = this.getCompiledWith((await this.config)?.compilerOptions);
}
return this.compileResult;
}
async getCompiledWith(options = {}) {
return this.parent.compiler.compile((await this.getTranspiled()).getText(), options);
}
}
exports.SvelteDocument = SvelteDocument;
class TranspiledSvelteDocument {
static async create(document, config) {
if (!config?.preprocess) {
return new TranspiledSvelteDocument(document.getText());
}
const filename = document.getFilePath() || '';
const preprocessed = await document.compiler.preprocess(document.getText(), wrapPreprocessors(config?.preprocess), {
filename
});
if (preprocessed.code === document.getText()) {
return new TranspiledSvelteDocument(document.getText());
}
return new TranspiledSvelteDocument(preprocessed.code, preprocessed.map
? new documents_1.SourceMapDocumentMapper(createTraceMap(preprocessed.map),
// The "sources" array only contains the Svelte filename, not its path.
// For getting generated positions, the sourcemap consumer wants an exact match
// of the source filepath. Therefore only pass in the filename here.
(0, utils_1.getLastPartOfPath)(filename))
: undefined);
}
constructor(code, mapper) {
this.code = code;
this.mapper = mapper;
}
getOriginalPosition(generatedPosition) {
return this.mapper?.getOriginalPosition(generatedPosition) || generatedPosition;
}
getText() {
return this.code;
}
getGeneratedPosition(originalPosition) {
return this.mapper?.getGeneratedPosition(originalPosition) || originalPosition;
}
}
exports.TranspiledSvelteDocument = TranspiledSvelteDocument;
/**
* Only used when the user has an old Svelte version installed where source map support
* for preprocessors is not built in yet.
* This fallback version does not map correctly when there's both a module and instance script.
* It isn't worth fixing these cases though now that Svelte ships a preprocessor with source maps.
*/
class FallbackTranspiledSvelteDocument {
static async create(document, preprocessors = []) {
const { transpiled, processedScripts, processedStyles } = await transpile(document, preprocessors);
const scriptMapper = SvelteFragmentMapper.createScript(document, transpiled, processedScripts);
const styleMapper = SvelteFragmentMapper.createStyle(document, transpiled, processedStyles);
return new FallbackTranspiledSvelteDocument(document, transpiled, scriptMapper, styleMapper);
}
constructor(parent, transpiled, scriptMapper, styleMapper) {
this.parent = parent;
this.transpiled = transpiled;
this.scriptMapper = scriptMapper;
this.styleMapper = styleMapper;
this.fragmentInfos = [this.scriptMapper?.fragmentInfo, this.styleMapper?.fragmentInfo]
.filter(utils_1.isNotNullOrUndefined)
.sort((i1, i2) => i1.end - i2.end);
}
getOriginalPosition(generatedPosition) {
if (this.scriptMapper?.isInTranspiledFragment(generatedPosition)) {
return this.scriptMapper.getOriginalPosition(generatedPosition);
}
if (this.styleMapper?.isInTranspiledFragment(generatedPosition)) {
return this.styleMapper.getOriginalPosition(generatedPosition);
}
// Position is not in fragments, but we still need to account for
// the length differences of the fragments before the position.
let offset = (0, documents_1.offsetAt)(generatedPosition, this.transpiled);
for (const fragmentInfo of this.fragmentInfos) {
if (offset > fragmentInfo.end) {
offset += fragmentInfo.diff;
}
}
return this.parent.positionAt(offset);
}
getURL() {
return this.parent.getURL();
}
getText() {
return this.transpiled;
}
getGeneratedPosition(originalPosition) {
const { styleInfo, scriptInfo } = this.parent;
if ((0, documents_1.isInTag)(originalPosition, scriptInfo) && this.scriptMapper) {
return this.scriptMapper.getGeneratedPosition(originalPosition);
}
if ((0, documents_1.isInTag)(originalPosition, styleInfo) && this.styleMapper) {
return this.styleMapper.getGeneratedPosition(originalPosition);
}
// Add length difference of each fragment
let offset = (0, documents_1.offsetAt)(originalPosition, this.parent.getText());
for (const fragmentInfo of this.fragmentInfos) {
if (offset > fragmentInfo.end) {
offset -= fragmentInfo.diff;
}
}
return (0, documents_1.positionAt)(offset, this.getText());
}
}
exports.FallbackTranspiledSvelteDocument = FallbackTranspiledSvelteDocument;
class SvelteFragmentMapper {
static createStyle(originalDoc, transpiled, processed) {
return SvelteFragmentMapper.create(originalDoc, transpiled, originalDoc.styleInfo, (0, documents_1.extractStyleTag)(transpiled), processed);
}
static createScript(originalDoc, transpiled, processed) {
const scriptInfo = originalDoc.scriptInfo || originalDoc.moduleScriptInfo;
const maybeScriptTag = (0, documents_1.extractScriptTags)(transpiled);
const maybeScriptTagInfo = maybeScriptTag && (maybeScriptTag.script || maybeScriptTag.moduleScript);
return SvelteFragmentMapper.create(originalDoc, transpiled, scriptInfo, maybeScriptTagInfo || null, processed);
}
static create(originalDoc, transpiled, originalTagInfo, transpiledTagInfo, processed) {
const sourceMapper = processed.length > 0
? SvelteFragmentMapper.createSourceMapper(processed, originalDoc)
: new documents_1.IdentityMapper(originalDoc.uri);
if (originalTagInfo && transpiledTagInfo) {
const sourceLength = originalTagInfo.container.end - originalTagInfo.container.start;
const transpiledLength = transpiledTagInfo.container.end - transpiledTagInfo.container.start;
const diff = sourceLength - transpiledLength;
return new SvelteFragmentMapper({ end: transpiledTagInfo.container.end, diff }, new documents_1.FragmentMapper(originalDoc.getText(), originalTagInfo, originalDoc.uri), new documents_1.FragmentMapper(transpiled, transpiledTagInfo, originalDoc.uri), sourceMapper);
}
return null;
}
static createSourceMapper(processed, originalDoc) {
return processed.reduce((parent, processedSingle) => processedSingle?.map
? new documents_1.SourceMapDocumentMapper(createTraceMap(processedSingle.map), originalDoc.uri, parent)
: new documents_1.IdentityMapper(originalDoc.uri, parent), undefined);
}
constructor(
/**
* End offset + length difference to original
*/
fragmentInfo,
/**
* Maps between full original source and fragment within that original.
*/
originalFragmentMapper,
/**
* Maps between full transpiled source and fragment within that transpiled.
*/
transpiledFragmentMapper,
/**
* Maps between original and transpiled, within fragment.
*/
sourceMapper) {
this.fragmentInfo = fragmentInfo;
this.originalFragmentMapper = originalFragmentMapper;
this.transpiledFragmentMapper = transpiledFragmentMapper;
this.sourceMapper = sourceMapper;
}
isInTranspiledFragment(generatedPosition) {
return this.transpiledFragmentMapper.isInGenerated(generatedPosition);
}
getOriginalPosition(generatedPosition) {
// Map the position to be relative to the transpiled fragment
const positionInTranspiledFragment = this.transpiledFragmentMapper.getGeneratedPosition(generatedPosition);
// Map the position, using the sourcemap, to the original position in the source fragment
const positionInOriginalFragment = this.sourceMapper.getOriginalPosition(positionInTranspiledFragment);
// Map the position to be in the original fragment's parent
return this.originalFragmentMapper.getOriginalPosition(positionInOriginalFragment);
}
/**
* Reversing `getOriginalPosition`
*/
getGeneratedPosition(originalPosition) {
const positionInOriginalFragment = this.originalFragmentMapper.getGeneratedPosition(originalPosition);
const positionInTranspiledFragment = this.sourceMapper.getGeneratedPosition(positionInOriginalFragment);
return this.transpiledFragmentMapper.getOriginalPosition(positionInTranspiledFragment);
}
}
exports.SvelteFragmentMapper = SvelteFragmentMapper;
/**
* Wrap preprocessors and rethrow on errors with more info on where the error came from.
*/
function wrapPreprocessors(preprocessors = []) {
preprocessors = Array.isArray(preprocessors) ? preprocessors : [preprocessors];
return preprocessors.map((preprocessor) => {
const wrappedPreprocessor = { markup: preprocessor.markup };
if (preprocessor.script) {
wrappedPreprocessor.script = async (args) => {
try {
return await preprocessor.script(args);
}
catch (e) {
e.__source = TranspileErrorSource.Script;
throw e;
}
};
}
if (preprocessor.style) {
wrappedPreprocessor.style = async (args) => {
try {
return await preprocessor.style(args);
}
catch (e) {
e.__source = TranspileErrorSource.Style;
throw e;
}
};
}
return wrappedPreprocessor;
});
}
async function transpile(document, preprocessors = []) {
preprocessors = Array.isArray(preprocessors) ? preprocessors : [preprocessors];
const processedScripts = [];
const processedStyles = [];
const wrappedPreprocessors = preprocessors.map((preprocessor) => {
const wrappedPreprocessor = { markup: preprocessor.markup };
if (preprocessor.script) {
wrappedPreprocessor.script = async (args) => {
try {
const res = await preprocessor.script(args);
if (res && res.map) {
processedScripts.push(res);
}
return res;
}
catch (e) {
e.__source = TranspileErrorSource.Script;
throw e;
}
};
}
if (preprocessor.style) {
wrappedPreprocessor.style = async (args) => {
try {
const res = await preprocessor.style(args);
if (res && res.map) {
processedStyles.push(res);
}
return res;
}
catch (e) {
e.__source = TranspileErrorSource.Style;
throw e;
}
};
}
return wrappedPreprocessor;
});
const result = await document.compiler.preprocess(document.getText(), wrappedPreprocessors, {
filename: document.getFilePath() || ''
});
const transpiled = result.code || result.toString?.() || '';
return { transpiled, processedScripts, processedStyles };
}
function createTraceMap(map) {
return new trace_mapping_1.TraceMap(normalizeMap(map));
function normalizeMap(map) {
// We don't know what we get, could be a stringified sourcemap,
// or a class which has the required properties on it, or a class
// which we need to call toString() on to get the correct format.
if (typeof map === 'string' || map.version) {
return map;
}
return map.toString();
}
}
//# sourceMappingURL=SvelteDocument.js.map