mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-06 17:05:34 +00:00
158 lines
No EOL
4.8 KiB
TypeScript
158 lines
No EOL
4.8 KiB
TypeScript
import * as fs from 'fs';
|
|
import path from 'path';
|
|
import convertSourceMap from 'convert-source-map'; // Transforms sourcemaps among various common formats.
|
|
// Base64, stringified-JSON, end-of-file comment...
|
|
|
|
// Because we're compiling the main project based on its TS build outputs, and our cross-module references
|
|
// also link to build outputs, we need to clean up the sourcemap-paths. Also, the individual module sourcemaps.
|
|
|
|
export interface Mapping {
|
|
readonly from: RegExp | string;
|
|
readonly to: string;
|
|
}
|
|
|
|
/**
|
|
* An intermediate state for sourcemap source path remapping operations + functions to
|
|
* mutate it.
|
|
*/
|
|
class RemappingState {
|
|
constructor(srcmap: Object) {
|
|
this.sourceMap = srcmap;
|
|
this.resetChangedSourcepathTracking();
|
|
}
|
|
|
|
public readonly sourceMap: any;
|
|
private _unchangedSourcePaths: string[] = [];
|
|
|
|
public get unchangedSourcepaths() {
|
|
return this._unchangedSourcePaths;
|
|
}
|
|
|
|
public resetChangedSourcepathTracking() {
|
|
this._unchangedSourcePaths = [...this.sourceMap.sources];
|
|
}
|
|
|
|
/**
|
|
* The sourcemap's `sourceRoot` property.
|
|
*/
|
|
public get sourceRoot() {
|
|
return this.sourceMap.sourceRoot;
|
|
}
|
|
|
|
/**
|
|
* The sourcemap's `sourceRoot` property.
|
|
*/
|
|
public set sourceRoot(path: string) {
|
|
this.sourceMap.sourceRoot = path;
|
|
}
|
|
|
|
/**
|
|
* Searches for source-file paths that match specified path-remapping rules and remaps them
|
|
* accordingly.
|
|
* @param transforms Path-remapping rules to apply. For each remappable path, only the first
|
|
* matching rule in the specified set will be applied.
|
|
* @returns `this` - the original instance - in order to facilitate function-call chaining.
|
|
*/
|
|
remapPaths(transforms: Mapping | Mapping[], logger?: (from: string, to: string) => void) {
|
|
if(!(transforms instanceof Array)) {
|
|
transforms = [transforms];
|
|
}
|
|
|
|
const unusedTransforms = [...transforms];
|
|
|
|
const sourcePaths = this.sourceMap.sources;
|
|
for(let i = 0; i < sourcePaths.length; i++) {
|
|
for(const transform of transforms) {
|
|
if(sourcePaths[i].match(transform.from)) {
|
|
// Note that this source path has been remapped (if it wasn't previously)
|
|
if(this.unchangedSourcepaths.includes(sourcePaths[i])) {
|
|
this.unchangedSourcepaths.splice(this.unchangedSourcepaths.indexOf(sourcePaths[i]), 1);
|
|
}
|
|
|
|
// Note that this transform has been used.
|
|
if(unusedTransforms.includes(transform)) {
|
|
unusedTransforms.splice(unusedTransforms.indexOf(transform), 1);
|
|
}
|
|
|
|
let startPath = sourcePaths[i];
|
|
sourcePaths[i] = sourcePaths[i].replace(transform.from, transform.to);
|
|
if(logger) {
|
|
logger(startPath, sourcePaths[i]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(unusedTransforms.length > 0) {
|
|
console.warn();
|
|
console.warn("Unused sourcemath mappings:");
|
|
for(const transform of unusedTransforms) {
|
|
let isRegex = transform.from instanceof RegExp;
|
|
let delim = isRegex ? '' : '"';
|
|
console.warn(`* ${delim}${transform.from.toString()}${delim} => "${transform.to}"`);
|
|
}
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Prepends all `sources` paths with the current sourceRoot (or just the part after a
|
|
* specified index).
|
|
* @param index
|
|
*/
|
|
public normalizeWithSourceRoot(index?: number) {
|
|
const sources = this.sourceMap.sources;
|
|
const originalSrcRoot = this.sourceRoot;
|
|
|
|
index = index || 0;
|
|
const prefix = originalSrcRoot.substring(index);
|
|
this.sourceRoot = originalSrcRoot.substring(0, index);
|
|
|
|
for(let i=0; i < sources.length; i++) {
|
|
// Normalizes, then swaps Windows '\\' entries with '/' if they exist;
|
|
// path.normalize respects Windows too much for our taste here.
|
|
sources[i] = path.normalize(`${prefix}/${sources[i]}`).replace(/\\/g, '/');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Writes the current state of the remapped sourcemap out to the specified file.
|
|
* @param file
|
|
*/
|
|
public toFile(file: fs.PathLike) {
|
|
fs.writeFileSync(file, convertSourceMap.fromObject(this.sourceMap).toJSON(2)); // 2 = space count
|
|
}
|
|
}
|
|
|
|
export default class SourcemapRemapper {
|
|
private constructor() {}
|
|
|
|
/**
|
|
* Starts sourcemap source path remapping from a pre-loaded JSON sourcemap instance.
|
|
* @param obj
|
|
* @returns
|
|
*/
|
|
public static fromObject(obj: Object) {
|
|
return new RemappingState(obj);
|
|
}
|
|
|
|
/**
|
|
* Starts sourcemap source path remapping from the specified .js.map file.
|
|
* @param file
|
|
* @returns
|
|
*/
|
|
public static fromFile(file: fs.PathLike) {
|
|
return this.fromBuffer(fs.readFileSync(file));
|
|
}
|
|
|
|
/**
|
|
* Starts sourcemap source path remapping from a loaded but unparsed file.
|
|
* @param buffer
|
|
* @returns
|
|
*/
|
|
public static fromBuffer(buffer: Buffer) {
|
|
return this.fromObject(convertSourceMap.fromJSON(buffer).toObject());
|
|
}
|
|
} |