@travetto/compiler 3.0.0-rc.11 → 3.0.0-rc.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/trv.js CHANGED
@@ -1,81 +1,96 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // @ts-check
4
+ import fs from 'fs/promises';
5
+ import path from 'path';
6
+ import { createRequire } from 'module';
7
+
8
+ import { getManifestContext } from '@travetto/manifest/bin/context.js';
9
+
10
+ const VALID_OPS = { watch: 'watch', build: 'build', clean: 'clean', manifest: 'manifest' };
4
11
 
5
12
  /**
6
13
  * @param {import('@travetto/manifest').ManifestContext} ctx
7
- * @return {Promise<import('../support/bin/compiler-bootstrap')>}
14
+ * @return {Promise<import('@travetto/compiler/support/launcher')>}
8
15
  */
9
- async function $getBootstrap(ctx) {
10
- const path = require('path');
11
- const { buildPackage } = require('./transpile');
16
+ const $getLauncher = async (ctx) => {
17
+ const compPkg = createRequire(path.resolve('node_modules')).resolve('@travetto/compiler/package.json');
18
+ const files = [];
12
19
 
13
- const root = path.resolve(__dirname, '..');
20
+ for (const file of ['support/launcher.js', 'support/transpile.js', 'package.json']) {
21
+ const target = path.resolve(ctx.workspacePath, ctx.compilerFolder, 'node_modules', '@travetto/compiler', file);
22
+ const src = compPkg.replace('package.json', file.replace(/[.]js$/, '.ts'));
14
23
 
15
- const loc = await buildPackage(
16
- ctx, '__compiler_bootstrap__', root, 'support/bin/compiler-bootstrap.ts',
17
- ['support/bin/utils.ts', 'bin/transpile.js', 'package.json']
18
- );
24
+ const targetTime = await fs.stat(target).then(s => Math.max(s.mtimeMs, s.ctimeMs)).catch(() => 0);
25
+ const srcTime = await fs.stat(src).then(s => Math.max(s.mtimeMs, s.ctimeMs));
26
+ // If stale
27
+ if (srcTime > targetTime) {
28
+ const ts = (await import('typescript')).default;
29
+ const module = ctx.moduleType === 'module' ? ts.ModuleKind.ESNext : ts.ModuleKind.CommonJS;
30
+ await fs.mkdir(path.dirname(target), { recursive: true });
31
+ const text = await fs.readFile(src, 'utf8');
32
+ if (file.endsWith('.js')) {
33
+ let content = ts.transpile(text, {
34
+ target: ts.ScriptTarget.ES2020, module, esModuleInterop: true, allowSyntheticDefaultImports: true
35
+ });
36
+ if (ctx.moduleType === 'module') {
37
+ content = content.replace(/^((?:im|ex)port .*from '[.][^']+)(')/mg, (_, a, b) => `${a}.js${b}`)
38
+ .replace(/^(import [^\n]*from '[^.][^\n/]+[/][^\n/]+[/][^\n']+)(')/mg, (_, a, b) => `${a}.js${b}`);
39
+ }
40
+ await fs.writeFile(target, content, 'utf8');
41
+ } else {
42
+ const pkg = JSON.parse(text);
43
+ pkg.type = ctx.moduleType;
44
+ await fs.writeFile(target, JSON.stringify(pkg, null, 2), 'utf8');
45
+ }
46
+ // Compile
47
+ }
48
+ files.push(target);
49
+ }
19
50
 
20
- try { return require(loc); } catch { return import(loc); }
21
- }
51
+ try { return await require(files[0]); }
52
+ catch { return import(files[0]); }
53
+ };
22
54
 
23
55
  /**
24
- * @param {import('@travetto/manifest').ManifestContext} ctx
25
- * @param {boolean} [watch]
56
+ * Parse arguments
57
+ * @param {string[]} args
58
+ * @returns {{ op?: keyof typeof VALID_OPS, clean?: boolean, outputPath?: string, env?: string }}
26
59
  */
27
- const compile = (ctx, watch) => $getBootstrap(ctx).then(({ compile: go }) => go(ctx, watch));
28
-
29
- /** @param {string[]} args */
30
- async function exec(args) {
31
- const { getContext } = require('./transpile');
32
- const op = args.find(x => !x.startsWith('-'));
33
- /** @type {{clean?: boolean, quiet?: boolean}} */
34
- const flags = Object.fromEntries(args.filter(x => x.startsWith('-')).map(x => x.split('-').pop()).map(x => [x, true]));
60
+ function parseArgs(args) {
61
+ const op = VALID_OPS[args.find(x => !x.startsWith('-')) ?? ''];
62
+ return {
63
+ op,
64
+ clean: args.includes('--clean') || args.includes('-c'),
65
+ ...(op === 'manifest' ? { outputPath: args[1], env: args[2] } : {})
66
+ };
67
+ }
35
68
 
36
- const ctx = await getContext();
37
- const message = flags.quiet ? () => { } : console.log.bind(console);
69
+ const exec = async () => {
70
+ const ctx = await getManifestContext();
71
+ const { op, outputPath, env, ...flags } = parseArgs(process.argv.slice(2));
38
72
 
39
73
  // Clean if needed
40
74
  if (op === 'clean' || (op && flags.clean)) {
41
- const fs = require('fs/promises');
42
- await Promise.all([ctx.outputFolder, ctx.compilerFolder].map(folder =>
43
- fs.rm(`${ctx.workspacePath}/${folder}`, { force: true, recursive: true })));
44
- if (op === 'clean') {
45
- message(`Cleaned ${ctx.workspacePath}: [${ctx.outputFolder}, ${ctx.compilerFolder}]`);
75
+ for (const f of [ctx.outputFolder, ctx.compilerFolder]) {
76
+ await fs.rm(path.resolve(ctx.workspacePath, f), { force: true, recursive: true });
46
77
  }
47
78
  }
48
79
 
80
+ if (op === 'clean') { // Clean needs to not attempt to compile/load launcher
81
+ return console.log(`Cleaned ${ctx.workspacePath}: [${ctx.outputFolder}, ${ctx.compilerFolder}]`);
82
+ }
83
+
84
+ const { compile, launchMain, exportManifest } = await $getLauncher(ctx);
85
+
49
86
  switch (op) {
50
- case 'clean': break;
51
- case 'manifest': {
52
- const { writeManifest, buildManifest } = await $getBootstrap(ctx);
53
- const manifest = (await buildManifest(ctx)).manifest;
54
- await writeManifest(ctx, manifest);
55
- const output = `${ctx.workspacePath}/${ctx.outputFolder}/${ctx.manifestFile}`;
56
- message(`Wrote manifest ${output}`);
57
- break;
58
- }
87
+ case 'manifest': return exportManifest(ctx, outputPath ?? '', env);
59
88
  case 'watch':
60
- message(`Watching ${ctx.workspacePath} for changes...`);
61
- await compile(ctx, true);
62
- return;
63
- case 'build':
64
- await compile(ctx);
65
- message(`Built to ${ctx.workspacePath}/${ctx.outputFolder}`);
66
- break;
67
- default: {
68
- const path = require('path/posix');
69
- const { manifest } = await compile(ctx);
70
- const out = path.join(ctx.workspacePath, ctx.outputFolder);
71
- // TODO: Externalize somehow?
72
- const cliMain = path.join(out, manifest.modules['@travetto/cli'].output, 'support', 'main.cli.js');
73
- process.env.TRV_MANIFEST = ctx.mainModule;
74
- process.env.TRV_OUTPUT = out;
75
- await import(process.env.TRV_MAIN = cliMain);
76
- return;
77
- }
89
+ case 'build': return compile(ctx, op);
90
+ default:
91
+ await compile(ctx, op);
92
+ return launchMain(ctx);
78
93
  }
79
- }
94
+ };
80
95
 
81
- exec(process.argv.slice(2));
96
+ exec();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/compiler",
3
- "version": "3.0.0-rc.11",
3
+ "version": "3.0.0-rc.12",
4
4
  "description": "Compiler",
5
5
  "keywords": [
6
6
  "compiler",
@@ -13,6 +13,7 @@
13
13
  "email": "travetto.framework@gmail.com",
14
14
  "name": "Travetto Framework"
15
15
  },
16
+ "type": "module",
16
17
  "files": [
17
18
  "__index__.ts",
18
19
  "src",
@@ -29,13 +30,13 @@
29
30
  "directory": "module/compiler"
30
31
  },
31
32
  "dependencies": {
32
- "@travetto/manifest": "^3.0.0-rc.6",
33
- "@travetto/terminal": "^3.0.0-rc.5",
34
- "@travetto/transformer": "^3.0.0-rc.9",
35
- "@parcel/watcher": "^2.1.0"
33
+ "@parcel/watcher": "^2.1.0",
34
+ "@travetto/manifest": "^3.0.0-rc.7",
35
+ "@travetto/terminal": "^3.0.0-rc.6",
36
+ "@travetto/transformer": "^3.0.0-rc.10"
36
37
  },
37
38
  "peerDependencies": {
38
- "@travetto/cli": "^3.0.0-rc.8"
39
+ "@travetto/cli": "^3.0.0-rc.9"
39
40
  },
40
41
  "peerDependenciesMeta": {
41
42
  "@travetto/cli": {
package/src/compiler.ts CHANGED
@@ -1,12 +1,15 @@
1
+ import util from 'util';
2
+ import { install } from 'source-map-support';
1
3
  import ts from 'typescript';
2
4
  import fs from 'fs/promises';
3
- import path from 'path';
4
5
 
5
- import { ManifestState } from '@travetto/manifest';
6
6
  import { GlobalTerminal, TerminalProgressEvent } from '@travetto/terminal';
7
+ import { RootIndex, watchFolders } from '@travetto/manifest';
8
+ import { TransformerManager } from '@travetto/transformer';
7
9
 
8
10
  import { CompilerUtil } from './util';
9
11
  import { CompilerState } from './state';
12
+ import type { CompilerLogEvent } from '../support/transpile';
10
13
 
11
14
  export type TransformerProvider = {
12
15
  init(checker: ts.TypeChecker): void;
@@ -14,36 +17,45 @@ export type TransformerProvider = {
14
17
  };
15
18
 
16
19
  type EmitError = Error | readonly ts.Diagnostic[];
17
- type Emitter = (file: string, newProgram?: boolean) => EmitError | undefined;
20
+ type Emitter = (file: string, newProgram?: boolean) => Promise<EmitError | undefined>;
18
21
  type EmitEvent = { file: string, i: number, total: number, err?: EmitError };
19
22
 
23
+ function log(level: 'info' | 'debug', message: string, ...args: unknown[]): void {
24
+ if (process.send) {
25
+ const ev: CompilerLogEvent = [level, util.format(message, ...args)];
26
+ process.send(ev);
27
+ } else {
28
+ // eslint-disable-next-line no-console
29
+ console[level](message, ...args);
30
+ }
31
+ }
32
+
33
+ const debug = log.bind(null, 'debug');
34
+ const info = log.bind(null, 'info');
35
+
20
36
  /**
21
37
  * Compilation support
22
38
  */
23
39
  export class Compiler {
24
40
 
25
- #bootTsconfig: string;
41
+ /**
42
+ * Run compiler as a main entry point
43
+ */
44
+ static async main(): Promise<void> {
45
+ const [dirty, watch] = process.argv.slice(2);
46
+ install();
47
+ const dirtyFiles = (await fs.readFile(dirty, 'utf8')).split(/\n/).filter(x => !!x);
48
+ return new Compiler(dirtyFiles).run(watch === 'true');
49
+ }
50
+
26
51
  #state: CompilerState;
27
- #transformers: string[];
28
-
29
- init(manifestState: ManifestState): this {
30
- this.#state = new CompilerState(manifestState);
31
- this.#bootTsconfig = this.#state.resolveModuleFile('@travetto/compiler', 'tsconfig.trv.json');
32
-
33
- this.#transformers = this.state.modules.flatMap(
34
- x => (x.files.support ?? [])
35
- .filter(([f, type]) => type === 'ts' && f.startsWith('support/transformer.'))
36
- .map(([f]) =>
37
- path.resolve(
38
- this.#state.manifest.workspacePath,
39
- this.#state.manifest.compilerFolder,
40
- x.output,
41
- f.replace(/[.][tj]s$/, '.js')
42
- )
43
- )
44
- );
45
-
46
- return this;
52
+ #dirtyFiles: string[];
53
+
54
+ constructor(dirtyFiles: string[]) {
55
+ this.#state = new CompilerState(RootIndex.manifest);
56
+ this.#dirtyFiles = dirtyFiles[0] === '*' ?
57
+ this.#state.getAllFiles() :
58
+ dirtyFiles.map(f => this.#state.resolveInput(f));
47
59
  }
48
60
 
49
61
  get state(): CompilerState {
@@ -54,44 +66,27 @@ export class Compiler {
54
66
  * Watches local modules
55
67
  */
56
68
  async #watchLocalModules(emit: Emitter): Promise<() => Promise<void>> {
57
- const folders = this.state.modules.filter(x => x.local).map(x => x.source);
58
- const emitWithError = (file: string): void => {
59
- const err = emit(file, true);
69
+ const emitWithError = async (file: string): Promise<void> => {
70
+ const err = await emit(file, true);
60
71
  if (err) {
61
- console.error(CompilerUtil.buildTranspileError(file, err));
72
+ info('Compilation Error', CompilerUtil.buildTranspileError(file, err));
62
73
  } else {
63
- console.error('Compiled', file.split('node_modules/')[1]);
74
+ info(`Compiled ${file.split('node_modules/')[1]}`);
64
75
  }
65
76
  };
66
77
  const watcher = this.state.getWatcher({
67
- create: (inputFile) => emitWithError(inputFile),
68
- update: (inputFile) => emitWithError(inputFile),
78
+ create: emitWithError,
79
+ update: emitWithError,
69
80
  delete: (outputFile) => fs.unlink(outputFile).catch(() => { })
70
81
  });
71
- return CompilerUtil.fileWatcher(folders, watcher);
82
+ return watchFolders(RootIndex.getLocalInputFolders(), watcher, {
83
+ filter: ev => ev.file.endsWith('.ts') || ev.file.endsWith('.js'),
84
+ ignore: ['node_modules']
85
+ });
72
86
  }
73
87
 
74
88
  async createTransformerProvider(): Promise<TransformerProvider> {
75
- const { TransformerManager } = await import('@travetto/transformer');
76
- return TransformerManager.create(this.#transformers, this.state.manifest);
77
- }
78
-
79
- async writeRawFile(file: string, contents: string, mode?: string): Promise<void> {
80
- const outFile = path.resolve(
81
- this.#state.manifest.workspacePath,
82
- this.#state.manifest.outputFolder,
83
- file
84
- );
85
- await fs.mkdir(path.dirname(outFile), { recursive: true });
86
- await fs.writeFile(outFile, contents, { encoding: 'utf8', mode });
87
- }
88
-
89
- async outputInit(): Promise<void> {
90
- // Write manifest
91
- await this.writeRawFile(this.#state.manifest.manifestFile, JSON.stringify(this.state.manifest));
92
- // TODO: This needs to be isolated, just like in the bootstrap
93
- await this.writeRawFile('trv', '#!/bin/sh\nnode node_modules/@travetto/cli/support/main.cli.js $@\n', '755');
94
- await this.writeRawFile('trv.cmd', 'node node_modules/@travetto/cli/support/main.cli.js %*\n', '755');
89
+ return TransformerManager.create(this.state.transformers);
95
90
  }
96
91
 
97
92
  /**
@@ -101,28 +96,27 @@ export class Compiler {
101
96
  let program: ts.Program;
102
97
 
103
98
  const transformers = await this.createTransformerProvider();
104
- const options = await CompilerUtil.getCompilerOptions(
105
- path.resolve(
106
- this.#state.manifest.workspacePath,
107
- this.#state.manifest.outputFolder,
108
- ),
109
- this.#bootTsconfig,
110
- this.#state.manifest.workspacePath
111
- );
99
+ const options = await this.state.getCompilerOptions();
112
100
  const host = this.state.getCompilerHost(options);
113
101
 
114
- const emit = (file: string, needsNewProgram = program === undefined): EmitError | undefined => {
115
- if (needsNewProgram) {
116
- program = ts.createProgram({ rootNames: this.#state.getAllFiles(), host, options, oldProgram: program });
117
- transformers.init(program.getTypeChecker());
118
- }
102
+ const emit = async (file: string, needsNewProgram = program === undefined): Promise<EmitError | undefined> => {
119
103
  try {
120
- const result = program.emit(
121
- program.getSourceFile(file)!, host.writeFile, undefined, false, transformers.get()
122
- );
104
+ if (needsNewProgram) {
105
+ program = ts.createProgram({ rootNames: this.#state.getAllFiles(), host, options, oldProgram: program });
106
+ transformers.init(program.getTypeChecker());
107
+ }
108
+ if (file.endsWith('.json')) {
109
+ host.writeFile(file, host.readFile(file)!, false);
110
+ } else if (file.endsWith('.js')) {
111
+ host.writeFile(file, ts.transpile(host.readFile(file)!, options), false);
112
+ } else {
113
+ const result = program.emit(
114
+ program.getSourceFile(file)!, host.writeFile, undefined, false, transformers.get()
115
+ );
123
116
 
124
- if (result.diagnostics?.length) {
125
- return result.diagnostics;
117
+ if (result.diagnostics?.length) {
118
+ return result.diagnostics;
119
+ }
126
120
  }
127
121
  } catch (err) {
128
122
  if (err instanceof Error) {
@@ -141,45 +135,49 @@ export class Compiler {
141
135
  */
142
136
  async * emit(files: string[], emitter: Emitter): AsyncIterable<EmitEvent> {
143
137
  let i = 0;
144
- const manifest = this.#state.manifest;
145
138
  for (const file of files) {
146
- const err = emitter(file);
147
- const outputFile = file
148
- .replace(/[.]ts$/, '.js')
149
- .replace(manifest.compilerFolder, manifest.outputFolder);
150
- yield { file: outputFile, i: i += 1, err, total: files.length };
139
+ const err = await emitter(file);
140
+ const imp = file.replace(/.*node_modules\//, '');
141
+ yield { file: imp, i: i += 1, err, total: files.length };
151
142
  }
143
+ debug(`Compiled ${i} files`);
152
144
  }
153
145
 
154
146
  /**
155
147
  * Run the compiler
156
148
  */
157
149
  async run(watch?: boolean): Promise<void> {
158
- await this.outputInit();
150
+ debug('Compilation started');
151
+
159
152
  const emitter = await this.getCompiler();
160
153
  let failed = false;
161
154
 
155
+ debug('Compiler loaded');
156
+
162
157
  const resolveEmittedFile = ({ file, total, i, err }: EmitEvent): TerminalProgressEvent => {
163
158
  if (err) {
164
159
  failed = true;
165
160
  console.error(CompilerUtil.buildTranspileError(file, err));
166
161
  }
167
- return { idx: i, total, text: `Compiling [%idx/%total] -- ${file.split('node_modules/')[1]}` };
162
+ return { idx: i, total, text: `Compiling [%idx/%total] -- ${file}` };
168
163
  };
169
164
 
170
- let files = this.state.getDirtyFiles();
171
- if (!watch && !files.length) {
172
- files = this.state.getAllFiles();
173
- }
174
-
175
- if (files.length) {
176
- await GlobalTerminal.trackProgress(this.emit(files, emitter), resolveEmittedFile, { position: 'bottom' });
165
+ if (this.#dirtyFiles.length) {
166
+ await GlobalTerminal.trackProgress(this.emit(this.#dirtyFiles, emitter), resolveEmittedFile, { position: 'bottom' });
177
167
  if (failed) {
168
+ debug('Compilation failed');
178
169
  process.exit(1);
170
+ } else {
171
+ debug('Compilation succeeded');
179
172
  }
180
173
  }
181
174
 
182
175
  if (watch) {
176
+ if (!this.#dirtyFiles.length) {
177
+ const resolved = this.state.resolveInput(RootIndex.getModule('@travetto/manifest')!.files.src[0].sourceFile);
178
+ await emitter(resolved, true);
179
+ }
180
+ info('Watch is ready');
183
181
  await this.#watchLocalModules(emitter);
184
182
  await new Promise(r => setTimeout(r, 1000 * 60 * 60 * 24));
185
183
  }
package/src/state.ts CHANGED
@@ -1,36 +1,32 @@
1
1
  import ts from 'typescript';
2
- import { mkdirSync, readFileSync, writeFile } from 'fs';
2
+ import { readFileSync } from 'fs';
3
3
 
4
- import {
5
- path,
6
- ManifestModuleUtil, ManifestDelta, ManifestModule,
7
- ManifestModuleFileType, ManifestRoot, ManifestState
8
- } from '@travetto/manifest';
4
+ import { path, ManifestModuleUtil, ManifestModule, ManifestModuleFileType, ManifestRoot, WatchEvent } from '@travetto/manifest';
9
5
 
10
- import { CompilerUtil, FileWatchEvent } from './util';
6
+ import { CompilerUtil } from './util';
7
+ import { TranspileUtil } from '../support/transpile';
11
8
 
12
9
  const validFile = (type: ManifestModuleFileType): boolean => type === 'ts' || type === 'package-json' || type === 'js';
13
10
 
14
11
  export class CompilerState {
15
12
 
16
13
  #inputFiles: Set<string>;
17
- #relativeInputToSource = new Map<string, { source: string, module: ManifestModule }>();
18
14
  #inputToSource = new Map<string, string>();
15
+ #stagedOutputToOutput = new Map<string, string>();
19
16
  #inputToOutput = new Map<string, string | undefined>();
20
17
  #inputDirectoryToSource = new Map<string, string>();
21
- #sourceInputOutput = new Map<string, { input: string, output?: string, relativeInput: string, module: ManifestModule }>();
18
+ #sourceInputOutput = new Map<string, { source: string, input: string, stagedOutput?: string, output?: string, module: ManifestModule }>();
22
19
 
23
20
  #sourceContents = new Map<string, string | undefined>();
24
21
  #sourceFileObjects = new Map<string, ts.SourceFile>();
25
22
  #sourceHashes = new Map<string, number>();
26
23
 
27
24
  #manifest: ManifestRoot;
28
- #delta: ManifestDelta;
29
25
  #modules: ManifestModule[];
26
+ #transformers: string[];
30
27
 
31
- constructor({ manifest, delta }: ManifestState) {
28
+ constructor(manifest: ManifestRoot) {
32
29
  this.#manifest = manifest;
33
- this.#delta = delta;
34
30
  this.#modules = Object.values(this.#manifest.modules);
35
31
  this.#inputFiles = new Set(this.#modules.flatMap(
36
32
  x => [
@@ -46,14 +42,29 @@ export class CompilerState {
46
42
  .map(([f]) => this.registerInput(x, f))
47
43
  ));
48
44
 
49
- return this;
45
+ this.#transformers = this.#modules.flatMap(
46
+ x => (x.files.$transformer ?? []).map(([f]) =>
47
+ path.resolve(manifest.workspacePath, x.sourceFolder, f)
48
+ )
49
+ );
50
+ }
51
+
52
+ async getCompilerOptions(): Promise<ts.CompilerOptions> {
53
+ return {
54
+ ...await TranspileUtil.getCompilerOptions(this.#manifest),
55
+ outDir: this.#manifest.workspacePath, // Force to root
56
+ };
57
+ }
58
+
59
+ resolveInput(file: string): string {
60
+ return this.#sourceInputOutput.get(file)!.input;
50
61
  }
51
62
 
52
63
  registerInput(module: ManifestModule, moduleFile: string): string {
53
- const relativeInput = `${module.output}/${moduleFile}`;
54
- const sourceFile = `${module.source}/${moduleFile}`;
64
+ const relativeInput = `${module.outputFolder}/${moduleFile}`;
65
+ const sourceFile = path.toPosix(path.resolve(this.#manifest.workspacePath, module.sourceFolder, moduleFile));
55
66
  const sourceFolder = path.dirname(sourceFile);
56
- const inputFile = path.resolve(relativeInput);
67
+ const inputFile = path.resolve(this.#manifest.workspacePath, '##', relativeInput); // Ensure input is isolated
57
68
  const inputFolder = path.dirname(inputFile);
58
69
  const fileType = ManifestModuleUtil.getFileType(moduleFile);
59
70
  const outputFile = fileType === 'typings' ?
@@ -61,25 +72,32 @@ export class CompilerState {
61
72
  path.resolve(
62
73
  this.#manifest.workspacePath,
63
74
  this.#manifest.outputFolder,
64
- (fileType === 'ts' ? relativeInput.replace(/[.]ts$/, '.js') : relativeInput)
75
+ CompilerUtil.inputToOutput(relativeInput)
65
76
  );
66
77
 
78
+ // Rewrite stagedOutput to final output form
79
+ const stagedOutputFile = CompilerUtil.inputToOutput(inputFile);
80
+
67
81
  this.#inputToSource.set(inputFile, sourceFile);
68
- this.#sourceInputOutput.set(sourceFile, { input: inputFile, output: outputFile, relativeInput, module });
82
+ this.#sourceInputOutput.set(sourceFile, { source: sourceFile, input: inputFile, stagedOutput: stagedOutputFile, output: outputFile, module });
69
83
  this.#inputToOutput.set(inputFile, outputFile);
70
84
  this.#inputDirectoryToSource.set(inputFolder, sourceFolder);
71
- this.#relativeInputToSource.set(relativeInput, { source: sourceFile, module });
85
+
86
+ if (stagedOutputFile) {
87
+ this.#stagedOutputToOutput.set(stagedOutputFile, outputFile!);
88
+ this.#stagedOutputToOutput.set(`${stagedOutputFile}.map`, `${outputFile!}.map`);
89
+ }
72
90
 
73
91
  return inputFile;
74
92
  }
75
93
 
76
94
  removeInput(inputFile: string): void {
77
95
  const source = this.#inputToSource.get(inputFile)!;
78
- const { relativeInput } = this.#sourceInputOutput.get(source)!;
96
+ const { stagedOutput } = this.#sourceInputOutput.get(source)!;
97
+ this.#stagedOutputToOutput.delete(stagedOutput!);
79
98
  this.#sourceInputOutput.delete(source);
80
99
  this.#inputToSource.delete(inputFile);
81
100
  this.#inputToOutput.delete(inputFile);
82
- this.#relativeInputToSource.delete(relativeInput);
83
101
  this.#inputFiles.delete(inputFile);
84
102
  }
85
103
 
@@ -88,51 +106,29 @@ export class CompilerState {
88
106
  this.#sourceContents.delete(inputFile);
89
107
  }
90
108
 
91
- get manifest(): ManifestRoot {
92
- return this.#manifest;
93
- }
94
-
95
109
  get modules(): ManifestModule[] {
96
110
  return this.#modules;
97
111
  }
98
112
 
99
- getDirtyFiles(): string[] {
100
- if (this.#delta && Object.keys(this.#delta).length) { // If we have any changes
101
- const files: string[] = [];
102
- for (const [modName, events] of Object.entries(this.#delta)) {
103
- const mod = this.#manifest.modules[modName];
104
- for (const { file } of events) {
105
- const fileType = ManifestModuleUtil.getFileType(file);
106
- if (validFile(fileType)) {
107
- files.push(path.resolve(mod.output, file));
108
- }
109
- }
110
- }
111
- return files;
112
- } else {
113
- return [];
114
- }
113
+ get transformers(): string[] {
114
+ return this.#transformers;
115
115
  }
116
116
 
117
117
  getAllFiles(): string[] {
118
118
  return [...this.#inputFiles];
119
119
  }
120
120
 
121
- resolveModuleFile(module: string, file: string): string {
122
- return `${this.modules.find(m => m.name === module)!.source}/${file}`;
123
- }
124
-
125
121
  // Build watcher
126
122
  getWatcher(handler: {
127
123
  create: (inputFile: string) => void;
128
124
  update: (inputFile: string) => void;
129
125
  delete: (outputFile: string) => void;
130
- }): (ev: FileWatchEvent, folder: string) => void {
131
- const mods = Object.fromEntries(this.modules.map(x => [x.source, x]));
132
- return ({ path: sourceFile, type }: FileWatchEvent, folder: string): void => {
126
+ }): (ev: WatchEvent, folder: string) => void {
127
+ const mods = Object.fromEntries(this.modules.map(x => [path.resolve(this.#manifest.workspacePath, x.sourceFolder), x]));
128
+ return ({ file: sourceFile, action }: WatchEvent, folder: string): void => {
133
129
  const mod = mods[folder];
134
- const moduleFile = sourceFile.replace(`${mod.source}/`, '');
135
- switch (type) {
130
+ const moduleFile = sourceFile.includes(mod.sourceFolder) ? sourceFile.split(`${mod.sourceFolder}/`)[1] : sourceFile;
131
+ switch (action) {
136
132
  case 'create': {
137
133
  const fileType = ManifestModuleUtil.getFileType(moduleFile);
138
134
  if (validFile(fileType)) {
@@ -165,16 +161,6 @@ export class CompilerState {
165
161
  }
166
162
  }
167
163
  }
168
-
169
- // Update manifest on every change
170
- writeFile(
171
- path.resolve(
172
- this.#manifest.workspacePath,
173
- this.#manifest.outputFolder,
174
- this.#manifest.manifestFile
175
- ),
176
- JSON.stringify(this.#manifest),
177
- () => { });
178
164
  };
179
165
  }
180
166
 
@@ -202,14 +188,14 @@ export class CompilerState {
202
188
  sourceFiles?: readonly ts.SourceFile[],
203
189
  data?: ts.WriteFileCallbackData
204
190
  ): void => {
205
- mkdirSync(path.dirname(outputFile), { recursive: true });
206
191
  if (outputFile.endsWith('package.json')) {
207
- text = CompilerUtil.rewritePackageJSON(this.manifest, text, options);
192
+ text = CompilerUtil.rewritePackageJSON(this.#manifest, text, options);
208
193
  } else if (!options.inlineSourceMap && options.sourceMap && outputFile.endsWith('.map')) {
209
- text = CompilerUtil.rewriteSourceMap(text, f => this.#relativeInputToSource.get(f));
194
+ text = CompilerUtil.rewriteSourceMap(this.#manifest.workspacePath, text, f => this.#sourceInputOutput.get(this.#inputToSource.get(f)!));
210
195
  } else if (options.inlineSourceMap && CompilerUtil.isSourceMapUrlPosData(data)) {
211
- text = CompilerUtil.rewriteInlineSourceMap(text, f => this.#relativeInputToSource.get(f), data);
196
+ text = CompilerUtil.rewriteInlineSourceMap(this.#manifest.workspacePath, text, f => this.#sourceInputOutput.get(this.#inputToSource.get(f)!), data);
212
197
  }
198
+ outputFile = this.#stagedOutputToOutput.get(outputFile) ?? outputFile;
213
199
  ts.sys.writeFile(outputFile, text, bom);
214
200
  },
215
201
  getSourceFile: (inputFile: string, language: ts.ScriptTarget, __onErr?: unknown): ts.SourceFile => {