Skip to content

Commit

Permalink
wip: generate declaration-maps
Browse files Browse the repository at this point in the history
  • Loading branch information
nikku committed Feb 26, 2024
1 parent 3b91ba4 commit ba4bf39
Show file tree
Hide file tree
Showing 7 changed files with 358 additions and 47 deletions.
152 changes: 130 additions & 22 deletions lib/generate-types.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
import path from 'node:path';
import path, { resolve } from 'node:path';

import ts from 'typescript';

import remapping from '@ampproject/remapping';

/**
* @typedef { ts.CompilerHost } CompilerHost
* @typedef { ts.WriteFileCallback } WriteFileCallback
*
* @typedef { {
* _readFile: (fileName: string) => string,
* _writeFile: WriteFileCallback,
* ___cache: Record<string, string>
* } } CompilerHostExtension
*/

import preTransform from './pre-transform.js';

import postTransform from './post-transform.js';
Expand All @@ -13,56 +26,151 @@ import postTransform from './post-transform.js';
*/
export default function generateTypes(fileNames, options) {

const {
declarationMap,
sourceRoot
} = options;

const names = new Set(fileNames.map((p) => path.resolve(p)));

const host = ts.createCompilerHost(options);
const host = /** @type { CompilerHost & CompilerHostExtension } */ (
ts.createCompilerHost(options)
);

// @ts-expect-error
host._readFile = host.readFile;

host.___cache = {};

/**
* @param {string} fileName
*
* @return {string}
*/
host.readFile = (fileName) => {

// @ts-expect-error
let src = host._readFile(fileName);
if (host.___cache[fileName]) {
return host.___cache[fileName];
}

const code = host._readFile(fileName);
const map = declarationMap && host._readFile(fileName + '.map');

isVerbose(options, fileName) && console.debug('[generate-types] [pre]', fileName, src);
let transformed;

if (names.has(path.resolve(fileName))) {
isVerbose(options, fileName) && console.debug('[generate-types] [pre]', fileName, code);

try {
src = preTransform(src);
transformed = preTransform(code, declarationMap && {
inputSourceMap: map,
sourceFileName: fileName,
sourceMapName: fileName + '___pre',
sourceRoot
});

if (transformed.map) {
host.___cache[fileName + '___pre'] = JSON.stringify(transformed.map);
}

host.___cache[fileName] = transformed.code;
} catch (err) {
console.error(`failed to parse ${fileName} [pre] with contents ${src}`, err);
console.error(`failed to parse ${fileName} [pre] with contents ${code}`, err);
throw err;
}
}

isVerbose(options, fileName) && console.debug('[generate-types] [pre] [generated]', fileName, src);
isVerbose(options, fileName) && console.debug('[generate-types] [pre] [generated]', fileName, transformed.code);
}

return src;
return transformed?.code || code;
};

// @ts-expect-error
host._writeFile = host.writeFile;

host.writeFile = (fileName, text, ...args) => {
isVerbose(options, fileName) && console.debug('[generate-types] [post]', fileName, text);
host.writeFile = (fileName, text, writeByteOrderMark, onError, sourceFiles, ...args) => {

const [ sourceFile ] = sourceFiles;

const originalFileName = /** @type {string|null} */ (
'originalFileName' in sourceFile && sourceFile.originalFileName || null
);

let transformed;

if (fileName.endsWith('.d.ts.map')) {

try {
text = postTransform(text);
} catch (err) {
console.error(`failed to parse ${fileName} [post] with contents ${text}`, err);
throw err;
if ('originalFileName' in sourceFile) {
const originalFileName = sourceFile.originalFileName;

host.___cache[originalFileName + '___ts'] = text;
}

return;
}

isVerbose(options, fileName) && console.debug('[generate-types] [post] [generated]', fileName, text);
if (fileName.endsWith('.d.ts')) {

isVerbose(options, fileName) && console.debug('[generate-types] [post]', fileName, text);

try {
const sourceMap_pre_json = host.___cache[originalFileName + '___pre'];
const sourceMap_ts_json = host.___cache[originalFileName + '___ts'];

transformed = postTransform(text, sourceMap_pre_json && sourceMap_ts_json && {
sourceFileName: fileName + '___ts',
sourceMapName: fileName,
sourceRoot
});

if (transformed.map) {

const sourceMap_pre = JSON.parse(sourceMap_pre_json);
const sourceMap_ts = JSON.parse(sourceMap_ts_json);
const sourceMap_post = transformed.map;

const { sources, file } = sourceMap_ts;

// fix file references
sourceMap_ts.file = sourceMap_post.sources[0];

sourceMap_ts.sources = [ resolve(sourceMap_pre.file) ];
sourceMap_post.sources = [ resolve(sourceMap_ts.file) ];
sourceMap_pre.sources = [ resolve(sourceMap_pre.sources[0]) ];

sourceMap_ts.file = resolve(sourceMap_ts.file);
sourceMap_post.file = resolve(sourceMap_post.file);
sourceMap_pre.file = resolve(sourceMap_pre.file);

// @ts-expect-error
const mappedSourceMap = remapping([
sourceMap_post,
sourceMap_ts,
sourceMap_pre
], () => null);

// back to relative sources
mappedSourceMap.file = file;
mappedSourceMap.sources = sources;

false && console.log('CONCAT', originalFileName + '.pre + ts', {
sourceMap_pre,
sourceMap_ts,

// sourceMap_post,
sourceMap_combined: mappedSourceMap
});

host._writeFile(fileName + '.map', JSON.stringify(mappedSourceMap), writeByteOrderMark, onError, sourceFiles, ...args);
}

} catch (err) {
console.error(`failed to parse ${fileName} [post] with contents ${text}`, err);
throw err;
}

isVerbose(options, fileName) && console.debug('[generate-types] [post] [generated]', fileName, transformed.code);
}

// @ts-expect-error
host._writeFile(fileName, text, ...args);
host._writeFile(fileName, transformed?.code || text, writeByteOrderMark, onError, sourceFiles, ...args);
};

const program = ts.createProgram(fileNames, options, host);
Expand Down
21 changes: 16 additions & 5 deletions lib/post-transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ import {
replace as replaceJSDoc
} from './parsers/jsdoc.js';

/**
* @typedef { {
* inputSourceMap?: string,
* sourceFileName?: string,
* sourceMapName?: string,
* sourceRoot?: string
* } } TransformOptions
*/

/**
* @template T
*
Expand All @@ -35,11 +44,13 @@ function isPublic(m) {

/**
* @param {string} src
* @return {string}
* @param {TransformOptions} [options]
*
* @return { { code: string, map?: any } }
*/
export default function transform(src) {
export default function transform(src, options = {}) {

const ast = parseDts(src);
const ast = parseDts(src, options);
const body = path(ast).get('program', 'body');

const replacements = [];
Expand Down Expand Up @@ -445,7 +456,7 @@ declare function p${
}).join(',')
}) : ${ returnTag ? returnTag.type.value.slice(1, -1) : 'void' };`;

const func = parseDts(methodCode).program.body[0];
const func = parseDts(methodCode, options).program.body[0];

const builder = {
'ClassProperty': b.tsDeclareMethod,
Expand Down Expand Up @@ -620,7 +631,7 @@ declare function p${
}
}

return print(ast).code;
return print(ast, options);
}

function traverse(path, cb) {
Expand Down
40 changes: 29 additions & 11 deletions lib/pre-transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ import {
builders as b
} from 'ast-types';

/**
* @typedef { {
* inputSourceMap?: string,
* sourceFileName?: string,
* sourceMapName?: string,
* sourceRoot?: string
* } } TransformOptions
*/

function superCall() {
return b.expressionStatement.from({
'expression': {
Expand All @@ -28,7 +37,8 @@ function clazzConstructor(cls, comments) {
'kind': 'constructor',
'params': constructor.params,
'body': constructor.body,
'comments': comments
'comments': comments,
'loc': constructor.loc
});
}

Expand All @@ -50,7 +60,8 @@ function clazzDefinition(cls, ctor) {
'kind': 'method',
'params': m.member.params,
'body': m.member.body,
'comments': m.comments
'comments': m.comments,
'loc': m.member.loc
};
}

Expand All @@ -63,7 +74,8 @@ function clazzDefinition(cls, ctor) {
'name': m.name
},
'value': m.member,
'comments': m.comments
'comments': m.comments,
'loc': m.member.loc
};
})
];
Expand All @@ -81,7 +93,8 @@ function clazzDefinition(cls, ctor) {
'kind': 'method',
'params': m.member.params,
'body': m.member.body,
'comments': m.comments
'comments': m.comments,
'loc': m.member.loc
};
}

Expand All @@ -93,7 +106,8 @@ function clazzDefinition(cls, ctor) {
'name': m.name
},
'value': m.member,
'comments': m.comments
'comments': m.comments,
'loc': m.node.loc
};
});

Expand All @@ -112,7 +126,8 @@ function clazzDefinition(cls, ctor) {
...members,
...staticMembers
]
}
},
'loc': ctor.loc
});
}

Expand All @@ -132,6 +147,7 @@ function splitComment(cls) {
trailing: false,
type: 'CommentBlock',
value: '*\n * @param' + rest.join('* @param'),
loc: comment.loc
} ] : [],
additionalComments: [
...additionalComments,
Expand All @@ -140,7 +156,8 @@ function splitComment(cls) {
{
leading: true,
type: 'CommentBlock',
value: intro
value: intro,
loc: comment.loc
}
] : []
)
Expand All @@ -150,12 +167,13 @@ function splitComment(cls) {

/**
* @param {string} src
* @param {TransformOptions} [options]
*
* @return {string}
* @return { { code: string, map?: any } }
*/
export default function transform(src) {
export default function transform(src, options) {

const ast = parse(src);
const ast = parse(src, options);
const body = path(ast).get('program', 'body');

const inheritsImports = findInheritsImports(body);
Expand Down Expand Up @@ -219,7 +237,7 @@ export default function transform(src) {

inheritsImports.forEach(m => m.node.replace());

return print(ast).code;
return print(ast, options);
}

function findInheritsImports(nodes) {
Expand Down

0 comments on commit ba4bf39

Please sign in to comment.