@travetto/compiler
Advanced tools
Comparing version
@@ -50,3 +50,3 @@ // @ts-check | ||
if (!cmd.quiet) { | ||
console.log(`${Util.colorize.success('Successfully')} wrote ${Util.colorize.output(count)} files to ${Util.colorize.path(cmd.output)}`); | ||
console.log(`${Util.colorize.success('Successfully')} wrote ${Util.colorize.output(count)} files to ${Util.colorize.path(cmd.output || 'default')}`); | ||
} | ||
@@ -53,0 +53,0 @@ }); |
@@ -8,2 +8,2 @@ export * from './src/transform-util'; | ||
export * from './src/source'; | ||
export * from './src/compiler'; | ||
export * from './src/compiler'; |
@@ -13,3 +13,3 @@ { | ||
"dependencies": { | ||
"@travetto/base": "^0.6.0-rc.0", | ||
"@travetto/base": "^0.6.0-rc.1", | ||
"@types/source-map-support": "^0.4.1", | ||
@@ -43,4 +43,4 @@ "source-map-support": "^0.5.9" | ||
}, | ||
"version": "0.6.0-rc.0", | ||
"gitHead": "0ed35f2a779cb9b93139a953d84817083ae0e7f5" | ||
"version": "0.6.0-rc.1", | ||
"gitHead": "0d54304e4a9547027dc7b1675494974090d52936" | ||
} |
import { EventEmitter } from 'events'; | ||
import { AppInfo, Env, FsUtil, ScanApp, AppCache } from '@travetto/base'; | ||
import { Env, FsUtil, ScanApp, AppCache } from '@travetto/base'; | ||
import { TransformerManager } from './transformers'; | ||
import { CompilerUtil } from './util'; | ||
import { SourceManager } from './source'; | ||
@@ -26,13 +25,7 @@ import { ModuleManager } from './module'; | ||
const exclude = [/\.d\.ts$/]; // Definition files | ||
// Get Files proper like | ||
if (AppInfo.DEV_PACKAGES && AppInfo.DEV_PACKAGES.length) { | ||
exclude.push(new RegExp(`${CompilerUtil.LIBRARY_PATH}[\/](${AppInfo.DEV_PACKAGES.join('|')})[\/]`)); | ||
} | ||
this.transformerManager = new TransformerManager(this.cwd); | ||
this.moduleManager = new ModuleManager(this.cwd); | ||
this.sourceManager = new SourceManager(this.cwd, {}); | ||
this.presenceManager = new FilePresenceManager(this.cwd, Env.appRoots, | ||
this.presenceManager = new FilePresenceManager(this.cwd, [...Env.appRoots], | ||
{ | ||
@@ -53,3 +46,3 @@ added: (name: string) => { | ||
} | ||
}, exclude); | ||
}); | ||
} | ||
@@ -83,2 +76,12 @@ | ||
handleLoadFailure(action: string, fileName: string, err: Error) { | ||
if (Env.watch) { | ||
console.trace(`Error in ${action} ${fileName}, removing`); | ||
this.unload(fileName); | ||
this.events.emit('removed', fileName); | ||
} else { | ||
throw err; | ||
} | ||
} | ||
requireHandler(m: NodeModule, tsf: string) { | ||
@@ -95,23 +98,12 @@ const jsf = tsf.replace(/\.ts$/, '.js'); | ||
const content = this.sourceManager.get(tsf)!; | ||
try { | ||
const ret = this.moduleManager.compile(m, jsf, content); | ||
if (ret) { | ||
if (isNew) { | ||
this.events.emit('required-after', tsf); | ||
} | ||
} else { | ||
this.sourceManager.set(tsf, CompilerUtil.EMPTY_MODULE); | ||
const res = this.moduleManager.compile(m, jsf, content); | ||
if (isNew) { | ||
this.events.emit('required-after', tsf); | ||
} | ||
return ret; | ||
} catch (e) { | ||
if (e.message.startsWith('Cannot find module') || e.message.startsWith('Unable to load')) { | ||
const name = m.filename.replace(`${this.cwd}/`, ''); | ||
const err = new Error(`${e.message} ${e.message.includes('from') ? `[via ${name}]` : `from ${name}`}`); | ||
err.stack = err.stack; | ||
throw err; | ||
} else { | ||
throw e; | ||
} | ||
return res; | ||
} catch (err) { | ||
this.handleLoadFailure('transpiling', tsf, err); | ||
} | ||
} | ||
@@ -145,14 +137,18 @@ | ||
transpile(fileName: string, force = false) { | ||
const changed = this.sourceManager.transpile(fileName, { | ||
fileName, | ||
reportDiagnostics: true, | ||
transformers: this.transformerManager.transformers || {} | ||
}, force); | ||
try { | ||
const changed = this.sourceManager.transpile(fileName, { | ||
fileName, | ||
reportDiagnostics: true, | ||
transformers: this.transformerManager.transformers || {} | ||
}, force); | ||
if (changed && (force || this.presenceManager.isWatchedFileLoaded(fileName))) { | ||
// If file is already loaded, mark for reload | ||
this.markForReload(fileName); | ||
if (changed && (force || this.presenceManager.isWatchedFileLoaded(fileName))) { | ||
// If file is already loaded, mark for reload | ||
this.markForReload(fileName); | ||
} | ||
return changed; | ||
} catch (err) { | ||
this.handleLoadFailure('transpiling', fileName, err); | ||
} | ||
return changed; | ||
} | ||
@@ -159,0 +155,0 @@ |
@@ -34,5 +34,3 @@ /// <reference types="node" /> | ||
const p = Module._resolveFilename(request, parent); | ||
if (Env.watch) { // If in a state where imports can come and go | ||
console.error(`Unable to import ${p}, stubbing out`, e); | ||
} else if (e) { | ||
if (!(Env.watch || p.endsWith('.ext.ts'))) { | ||
// Marking file as being loaded, useful for the test framework | ||
@@ -42,4 +40,7 @@ require.cache[p] = {}; | ||
} | ||
if (Env.watch) { | ||
console.warn(`Unable to import ${p}, stubbing out`, e.message); | ||
} | ||
mod = {}; | ||
mod = CompilerUtil.getErrorModuleProxy(e.message); | ||
} | ||
@@ -52,3 +53,3 @@ | ||
const p = Module._resolveFilename(request, parent); | ||
if (p.includes(this.cwd) && !p.includes(CompilerUtil.LIBRARY_PATH)) { | ||
if (p.includes(this.cwd) && !p.includes('node_modules')) { | ||
if (!this.modules.has(p)) { | ||
@@ -73,14 +74,16 @@ const handler = new RetargettingHandler(mod); | ||
try { | ||
(m as any)._compile(content, jsf); | ||
return true; | ||
return (m as any)._compile(content, jsf); | ||
} catch (e) { | ||
if (Env.watch) { // If attempting to load an optional require | ||
console.error(`Unable to import ${name}, stubbing out`, e); | ||
(m as any)._compile(CompilerUtil.EMPTY_MODULE, jsf); | ||
return false; | ||
} else { | ||
throw e; | ||
if (Env.watch) { // If compiling fails, treat as recoverable in watch mode | ||
console.warn(`Unable to compile ${name}, stubbing out`, e.message); | ||
(m as any)._compile(CompilerUtil.getErrorModuleProxySource(e.message), jsf); | ||
} | ||
if (e.message.startsWith('Cannot find module') || e.message.startsWith('Unable to load')) { | ||
const modName = m.filename.replace(`${this.cwd}/`, ''); | ||
const err = new Error(`${e.message} ${e.message.includes('from') ? `[via ${modName}]` : `from ${modName}`}`); | ||
err.stack = err.stack; | ||
e = err; | ||
} | ||
throw e; | ||
} | ||
} | ||
@@ -87,0 +90,0 @@ |
@@ -17,5 +17,5 @@ import * as path from 'path'; | ||
constructor(private cwd: string, private rootPaths: string[], private listener: Listener, private excludeFiles: RegExp[], private watch: boolean = Env.watch) { | ||
constructor(private cwd: string, private rootPaths: string[], private listener: Listener, private excludeFiles: RegExp[] = [], private watch: boolean = Env.watch) { | ||
if (!this.rootPaths.includes('.')) { | ||
if (this.rootPaths.length && !this.rootPaths.includes('.')) { | ||
this.rootPaths.push('.'); | ||
@@ -28,3 +28,3 @@ } | ||
if (Env.watch) { | ||
if (watch) { | ||
Shutdown.onUnhandled(e => this.handleMissingModule(e), 0); | ||
@@ -36,3 +36,3 @@ } | ||
if (err && (err.message || '').includes('Cannot find module')) { // Handle module reloading | ||
Env.error(err); | ||
console.error(err); | ||
return true; | ||
@@ -107,2 +107,5 @@ } | ||
validFile(name: string) { | ||
if (name.endsWith('.d.ts')) { | ||
return false; | ||
} | ||
for (const re of this.excludeFiles) { | ||
@@ -109,0 +112,0 @@ if (re.test(name)) { |
@@ -30,29 +30,20 @@ import * as ts from 'typescript'; | ||
if (!res.diagnostics || !res.diagnostics.length) { | ||
return; | ||
} | ||
if (res.diagnostics && res.diagnostics.length) { | ||
const errors = res.diagnostics.slice(0, 5).map(diag => { | ||
const message = ts.flattenDiagnosticMessageText(diag.messageText, '\n'); | ||
if (diag.file) { | ||
const { line, character } = diag.file.getLineAndCharacterOfPosition(diag.start as number); | ||
return ` @ ${diag.file.fileName.replace(`${this.cwd}/`, '')}(${line + 1}, ${character + 1}): ${message}`; | ||
} else { | ||
return ` ${message}`; | ||
const errors = res.diagnostics.slice(0, 5).map(diag => { | ||
const message = ts.flattenDiagnosticMessageText(diag.messageText, '\n'); | ||
if (diag.file) { | ||
const { line, character } = diag.file.getLineAndCharacterOfPosition(diag.start as number); | ||
return ` @ ${diag.file.fileName.replace(`${this.cwd}/`, '')}(${line + 1}, ${character + 1}): ${message}`; | ||
} else { | ||
return ` ${message}`; | ||
} | ||
}); | ||
if (res.diagnostics.length > 5) { | ||
errors.push(`${res.diagnostics.length - 5} more ...`); | ||
} | ||
}); | ||
if (res.diagnostics.length > 5) { | ||
errors.push(`${res.diagnostics.length - 5} more ...`); | ||
throw new AppError(`Transpiling ${fileName.replace(`${this.cwd}/`, '')} failed`, 'unavailable', { errors }); | ||
} | ||
const msg = `Compiling ${fileName.replace(`${this.cwd}/`, '')} failed:\n [\n ${errors.join('\n ')}\n ]`; | ||
if (Env.watch) { // If attempting to load an optional require | ||
console.error(msg); | ||
console.error(`Unable to import ${fileName}, stubbing out`); | ||
this.set(fileName, CompilerUtil.EMPTY_MODULE); | ||
} else { | ||
throw new AppError(msg, 'unavailable'); | ||
} | ||
} | ||
@@ -59,0 +50,0 @@ |
@@ -178,2 +178,13 @@ import * as ts from 'typescript'; | ||
/* | ||
* useful for handling failed imports, but still transpiling | ||
*/ | ||
static optionalResolve(file: string) { | ||
try { | ||
return require.resolve(file); | ||
} catch { | ||
return file; | ||
} | ||
} | ||
static importingVisitor<T extends TransformerState>( | ||
@@ -200,3 +211,3 @@ init: (file: ts.SourceFile, context?: ts.TransformationContext) => Partial<T>, | ||
if (ts.isImportDeclaration(stmt) && ts.isStringLiteral(stmt.moduleSpecifier)) { | ||
let path = require.resolve(stmt.moduleSpecifier.text | ||
let path = this.optionalResolve(stmt.moduleSpecifier.text | ||
.replace(/^\.\./, dirname(dirname(state.path))) | ||
@@ -203,0 +214,0 @@ .replace(/^\.\//, `${dirname(state.path)}/`)); |
@@ -5,6 +5,32 @@ import * as ts from 'typescript'; | ||
export class CompilerUtil { | ||
static LIBRARY_PATH = 'node_modules'; | ||
static EMPTY_MODULE = 'module.exports = {}'; | ||
static getErrorModuleProxySource(err: string = 'Module is not found') { | ||
return this.getErrorModuleProxy.toString().split(/[(]err[)]\s*[{]/)[1] | ||
.replace(/[}]$/, '') | ||
.replace('err', `\`${err.replace(/[`]/g, `'`)}\``) | ||
.replace('return ', 'module.exports = '); | ||
} | ||
static getErrorModuleProxy(err: string) { | ||
const onError = () => { | ||
throw new Error(err); | ||
}; | ||
return new Proxy({}, { | ||
enumerate: () => [], | ||
isExtensible: () => false, | ||
getOwnPropertyDescriptor: () => ({}), | ||
preventExtensions: () => true, | ||
apply: onError, | ||
construct: onError, | ||
setPrototypeOf: onError, | ||
getPrototypeOf: onError, | ||
get: onError, | ||
has: onError, | ||
set: onError, | ||
ownKeys: onError, | ||
deleteProperty: onError, | ||
defineProperty: onError | ||
}); | ||
} | ||
static resolveOptions(dir: string, name: string = 'tsconfig.json') { | ||
@@ -11,0 +37,0 @@ const out = ts.parseJsonSourceFileConfigFileContent( |
@@ -5,7 +5,5 @@ export const init = { | ||
action: async () => { | ||
const compiler = require('../src/compiler').Compiler; | ||
await new Promise(r => setTimeout(r, 0)); | ||
const res = compiler.init(); | ||
return res; | ||
const { Compiler } = await import('../src/compiler'); | ||
Compiler.init(); | ||
} | ||
}; |
40368
1.9%1022
2.82%Updated