This commit is contained in:
Johannes
2022-12-02 13:03:16 +01:00
parent 9433821f09
commit f73bb21f27
7 changed files with 203 additions and 29 deletions

File diff suppressed because one or more lines are too long

View File

@@ -18,6 +18,7 @@ import ts = require('typescript');
import * as File from 'vinyl';
import * as task from './task';
import { Mangler } from './mangleTypeScript';
import { RawSourceMap } from 'source-map';
const watch = require('./watch');
@@ -125,10 +126,11 @@ export function compileTask(src: string, out: string, build: boolean): () => Nod
if (build) {
let ts2tsMangler = new Mangler(compile.projectPath);
const newContentsByFileName = ts2tsMangler.computeNewFileContents();
mangleStream = es.through(function write(data: File) {
mangleStream = es.through(function write(data: File & { sourceMap?: RawSourceMap }) {
const newContents = newContentsByFileName.get(data.path);
if (newContents !== undefined) {
data.contents = Buffer.from(newContents);
data.contents = Buffer.from(newContents.out);
data.sourceMap = newContents.sourceMap && JSON.parse(newContents.sourceMap);
}
this.push(data);
}, function end() {

File diff suppressed because one or more lines are too long

View File

@@ -7,6 +7,8 @@ import * as ts from 'typescript';
import * as path from 'path';
import * as fs from 'fs';
import { argv } from 'process';
import { SourceMapGenerator } from 'source-map';
import { pathToFileURL } from 'url';
class ShortIdent {
@@ -339,7 +341,7 @@ export class Mangler {
this.service = ts.createLanguageService(new StaticLanguageServiceHost(projectPath));
}
computeNewFileContents(): Map<string, string> {
computeNewFileContents(): Map<string, { out: string; sourceMap?: string }> {
// STEP: find all classes and their field info
@@ -467,11 +469,18 @@ export class Mangler {
this.log(`done preparing EDITS for ${editsByFile.size} files`);
// STEP: apply all rename edits (per file)
const result = new Map<string, string>();
const result = new Map<string, { out: string; sourceMap?: string }>();
let savedBytes = 0;
for (const item of this.service.getProgram()!.getSourceFiles()) {
const { mapRoot, sourceRoot } = this.service.getProgram()!.getCompilerOptions();
const projectDir = path.dirname(this.projectPath);
const sourceMapRoot = mapRoot ?? pathToFileURL(sourceRoot ?? projectDir).toString();
// source maps
let generator: SourceMapGenerator | undefined;
let newFullText: string;
const edits = editsByFile.get(item.fileName);
if (!edits) {
@@ -479,6 +488,13 @@ export class Mangler {
newFullText = item.getFullText();
} else {
// source map generator
const relativeFileName = path.relative(projectDir, item.fileName);
generator = new SourceMapGenerator({
file: path.basename(item.fileName),
sourceRoot: sourceMapRoot
});
// apply renames
edits.sort((a, b) => b.offset - a.offset);
const characters = item.getFullText().split('');
@@ -498,12 +514,27 @@ export class Mangler {
}
}
lastEdit = edit;
const removed = characters.splice(edit.offset, edit.length, edit.newText);
savedBytes += removed.length - edit.newText.length;
const mangledName = characters.splice(edit.offset, edit.length, edit.newText).join('');
savedBytes += mangledName.length - edit.newText.length;
// source maps
const pos = item.getLineAndCharacterOfPosition(edit.offset);
generator.addMapping({
source: relativeFileName,
original: { line: pos.line + 1, column: pos.character },
generated: { line: pos.line + 1, column: pos.character },
name: mangledName
});
generator.addMapping({
source: relativeFileName,
original: { line: pos.line + 1, column: pos.character + edit.length },
generated: { line: pos.line + 1, column: pos.character + edit.newText.length }
});
generator.setSourceContent(relativeFileName, item.getFullText());
}
newFullText = characters.join('');
}
result.set(item.fileName, newFullText);
result.set(item.fileName, { out: newFullText, sourceMap: generator?.toString() });
}
this.log(`DONE saved ${savedBytes / 1000}kb`);
@@ -523,12 +554,15 @@ async function _run() {
const projectPath = path.join(__dirname, '../../src/tsconfig.json');
const projectBase = path.dirname(projectPath);
const newProjectBase = path.join(path.dirname(projectBase), path.basename(projectBase) + '-mangle');
const newProjectBase = path.join(path.dirname(projectBase), path.basename(projectBase) + '2');
for await (const [fileName, contents] of new Mangler(projectPath, console.log).computeNewFileContents()) {
const newFilePath = path.join(newProjectBase, path.relative(projectBase, fileName));
await fs.promises.mkdir(path.dirname(newFilePath), { recursive: true });
await fs.promises.writeFile(newFilePath, contents);
await fs.promises.writeFile(newFilePath, contents.out);
if (contents.sourceMap) {
await fs.promises.writeFile(newFilePath + '.map', contents.sourceMap);
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -3,13 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { statSync, readFileSync } from 'fs';
import * as fs from 'fs';
import * as path from 'path';
import * as crypto from 'crypto';
import * as utils from './utils';
import * as colors from 'ansi-colors';
import * as ts from 'typescript';
import * as Vinyl from 'vinyl';
import { RawSourceMap, SourceMapConsumer, SourceMapGenerator } from 'source-map';
export interface IConfiguration {
logFn: (topic: string, message: string) => void;
@@ -158,8 +159,63 @@ export function createTypeScriptBuilder(config: IConfiguration, projectFile: str
const dirname = path.dirname(vinyl.relative);
const tsname = (dirname === '.' ? '' : dirname + '/') + basename + '.ts';
const sourceMap = JSON.parse(sourcemapFile.text);
let sourceMap = <RawSourceMap>JSON.parse(sourcemapFile.text);
sourceMap.sources[0] = tsname.replace(/\\/g, '/');
// check for an input source map and combine them
const snapshot = host.getScriptSnapshot(fileName);
if (snapshot instanceof VinylScriptSnapshot && snapshot.sourceMap) {
const inputSMC = new SourceMapConsumer(snapshot.sourceMap);
const tsSMC = new SourceMapConsumer(sourceMap);
let didChange = false;
const smg = new SourceMapGenerator({
file: sourceMap.file,
sourceRoot: sourceMap.sourceRoot
});
tsSMC.eachMapping(m => {
didChange = true;
const original = { line: m.originalLine, column: m.originalColumn };
const generated = { line: m.generatedLine, column: m.generatedColumn };
// JS-out position -> input original position
const inputOriginal = inputSMC.originalPositionFor(original);
if (inputOriginal.source !== null) {
const inputSource = inputOriginal.source;
smg.addMapping({
source: inputSource,
name: inputOriginal.name,
generated: generated,
original: inputOriginal
});
smg.setSourceContent(
inputSource,
inputSMC.sourceContentFor(inputSource)
);
} else {
smg.addMapping({
source: m.source,
name: m.name,
generated: generated,
original: original
});
smg.setSourceContent(
m.source,
tsSMC.sourceContentFor(m.source)
);
}
}, null, SourceMapConsumer.GENERATED_ORDER);
if (didChange) {
sourceMap = JSON.parse(smg.toString());
// const filename = '/Users/jrieken/Code/vscode/src2/' + vinyl.relative + '.map';
// fs.promises.mkdir(path.dirname(filename), { recursive: true }).then(async () => {
// await fs.promises.writeFile(filename, smg.toString());
// await fs.promises.writeFile('/Users/jrieken/Code/vscode/src2/' + vinyl.relative, vinyl.contents);
// });
}
}
(<any>vinyl).sourceMap = sourceMap;
}
}
@@ -397,9 +453,12 @@ class VinylScriptSnapshot extends ScriptSnapshot {
private readonly _base: string;
constructor(file: Vinyl) {
readonly sourceMap?: RawSourceMap;
constructor(file: Vinyl & { sourceMap?: RawSourceMap }) {
super(file.contents!.toString(), file.stat!.mtime);
this._base = file.base;
this.sourceMap = file.sourceMap;
}
getBase(): string {
@@ -474,9 +533,9 @@ class LanguageServiceHost implements ts.LanguageServiceHost {
try {
result = new VinylScriptSnapshot(new Vinyl(<any>{
path: filename,
contents: readFileSync(filename),
contents: fs.readFileSync(filename),
base: this.getCompilationSettings().outDir,
stat: statSync(filename)
stat: fs.statSync(filename)
}));
this.addScriptSnapshot(filename, result);
} catch (e) {

View File

@@ -11,7 +11,7 @@ const { Mangler } = require('../build/lib/mangleTypeScript');
/**
* Map of project paths to mangled file contents
*
* @type {Map<string, Map<string, string>>}
* @type {Map<string, Map<string, { out: string; sourceMap?: string }>>}
*/
const mangleMap = new Map();
@@ -46,5 +46,5 @@ module.exports = async function (source, sourceMap, meta) {
const fileContentsMap = getMangledFileContents(options.configFile);
const newContents = fileContentsMap.get(this.resourcePath);
callback(null, newContents ?? source, sourceMap, meta);
callback(null, newContents?.out ?? source, sourceMap, meta);
};