@esportsplus/typescript 0.24.2 → 0.25.1

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/build/cli/tsc.js CHANGED
@@ -3,9 +3,10 @@ import { createRequire } from 'module';
3
3
  import { pathToFileURL } from 'url';
4
4
  import path from 'path';
5
5
  import ts from 'typescript';
6
+ import coordinator from '../compiler/coordinator.js';
6
7
  const BACKSLASH_REGEX = /\\/g;
7
8
  let require = createRequire(import.meta.url), skipFlags = new Set(['--help', '--init', '--noEmit', '--showConfig', '--version', '-h', '-noEmit', '-v']);
8
- async function build(config, tsconfig, plugins) {
9
+ async function build(config, tsconfig, pluginConfigs) {
9
10
  let root = path.dirname(path.resolve(tsconfig)), parsed = ts.parseJsonConfigFileContent(config, ts.sys, root);
10
11
  if (parsed.errors.length > 0) {
11
12
  for (let i = 0, n = parsed.errors.length; i < n; i++) {
@@ -13,63 +14,50 @@ async function build(config, tsconfig, plugins) {
13
14
  }
14
15
  process.exit(1);
15
16
  }
16
- await loadPlugins(plugins, root).then((factories) => {
17
- let context = new Map(), printer = ts.createPrinter(), program = ts.createProgram(parsed.fileNames, parsed.options), transformedFiles = new Map();
18
- let instances = factories.before.map(f => f(program, context));
19
- for (let i = 0, n = parsed.fileNames.length; i < n; i++) {
20
- let fileName = parsed.fileNames[i], sourceFile = program.getSourceFile(fileName);
21
- if (!sourceFile) {
22
- continue;
23
- }
24
- for (let j = 0, m = instances.length; j < m; j++) {
25
- instances[j].analyze?.(sourceFile);
26
- }
27
- }
28
- let transformers = instances.map(i => i.transform);
29
- for (let i = 0, n = parsed.fileNames.length; i < n; i++) {
30
- let fileName = parsed.fileNames[i], sourceFile = program.getSourceFile(fileName);
31
- if (!sourceFile) {
32
- continue;
33
- }
34
- let result = ts.transform(sourceFile, transformers), transformed = result.transformed[0];
35
- if (transformed !== sourceFile) {
36
- transformedFiles.set(normalizePath(fileName), printer.printFile(transformed));
37
- }
38
- result.dispose();
17
+ let plugins = await loadPlugins(pluginConfigs, root);
18
+ let program = ts.createProgram(parsed.fileNames, parsed.options), shared = new Map(), transformedFiles = new Map();
19
+ for (let i = 0, n = parsed.fileNames.length; i < n; i++) {
20
+ let fileName = parsed.fileNames[i], sourceFile = program.getSourceFile(fileName);
21
+ if (!sourceFile) {
22
+ continue;
39
23
  }
40
- if (transformedFiles.size > 0) {
41
- let customHost = ts.createCompilerHost(parsed.options), originalGetSourceFile = customHost.getSourceFile.bind(customHost), originalReadFile = customHost.readFile.bind(customHost);
42
- customHost.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
43
- let transformed = transformedFiles.get(normalizePath(fileName));
44
- if (transformed) {
45
- return ts.createSourceFile(fileName, transformed, languageVersion, true);
46
- }
47
- return originalGetSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
48
- };
49
- customHost.readFile = (fileName) => {
50
- return transformedFiles.get(normalizePath(fileName)) ?? originalReadFile(fileName);
51
- };
52
- program = ts.createProgram(parsed.fileNames, parsed.options, customHost);
24
+ let result = coordinator.transform(plugins, sourceFile.getFullText(), sourceFile, program, shared);
25
+ if (result.changed) {
26
+ transformedFiles.set(normalizePath(fileName), result.code);
53
27
  }
54
- let { diagnostics, emitSkipped } = program.emit();
55
- diagnostics = ts.getPreEmitDiagnostics(program).concat(diagnostics);
56
- if (diagnostics.length > 0) {
57
- console.error(ts.formatDiagnosticsWithColorAndContext(diagnostics, {
58
- getCanonicalFileName: (fileName) => fileName,
59
- getCurrentDirectory: () => root,
60
- getNewLine: () => '\n'
61
- }));
62
- }
63
- if (emitSkipped) {
64
- process.exit(1);
65
- }
66
- return runTscAlias(process.argv.slice(2)).then((code) => process.exit(code));
67
- });
28
+ }
29
+ if (transformedFiles.size > 0) {
30
+ let customHost = ts.createCompilerHost(parsed.options), originalGetSourceFile = customHost.getSourceFile.bind(customHost), originalReadFile = customHost.readFile.bind(customHost);
31
+ customHost.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
32
+ let transformed = transformedFiles.get(normalizePath(fileName));
33
+ if (transformed) {
34
+ return ts.createSourceFile(fileName, transformed, languageVersion, true);
35
+ }
36
+ return originalGetSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
37
+ };
38
+ customHost.readFile = (fileName) => {
39
+ return transformedFiles.get(normalizePath(fileName)) ?? originalReadFile(fileName);
40
+ };
41
+ program = ts.createProgram(parsed.fileNames, parsed.options, customHost);
42
+ }
43
+ let { diagnostics, emitSkipped } = program.emit();
44
+ diagnostics = ts.getPreEmitDiagnostics(program).concat(diagnostics);
45
+ if (diagnostics.length > 0) {
46
+ console.error(ts.formatDiagnosticsWithColorAndContext(diagnostics, {
47
+ getCanonicalFileName: (fileName) => fileName,
48
+ getCurrentDirectory: () => root,
49
+ getNewLine: () => '\n'
50
+ }));
51
+ }
52
+ if (emitSkipped) {
53
+ process.exit(1);
54
+ }
55
+ return runTscAlias(process.argv.slice(2)).then((code) => process.exit(code));
68
56
  }
69
- async function loadPlugins(plugins, root) {
70
- let after = [], before = [], promises = [];
71
- for (let i = 0, n = plugins.length; i < n; i++) {
72
- let plugin = plugins[i], pluginPath = plugin.transform;
57
+ async function loadPlugins(configs, root) {
58
+ let plugins = [], promises = [];
59
+ for (let i = 0, n = configs.length; i < n; i++) {
60
+ let config = configs[i], pluginPath = config.transform;
73
61
  if (pluginPath.startsWith('.')) {
74
62
  pluginPath = pathToFileURL(path.resolve(root, pluginPath)).href;
75
63
  }
@@ -77,20 +65,19 @@ async function loadPlugins(plugins, root) {
77
65
  pluginPath = pathToFileURL(require.resolve(pluginPath, { paths: [root] })).href;
78
66
  }
79
67
  promises.push(import(pluginPath).then((module) => {
80
- let factory = module.default ?? module.createTransformer ?? module;
81
- if (typeof factory !== 'function') {
82
- console.error(`Plugin ${plugin.transform}: no transformer factory found`);
83
- return;
84
- }
85
- if (plugin.after) {
86
- after.push(factory);
68
+ let plugin = module.default ?? module;
69
+ if (typeof plugin === 'function') {
70
+ plugin = plugin();
87
71
  }
88
- else {
89
- before.push(factory);
72
+ if (!plugin || typeof plugin.transform !== 'function') {
73
+ console.error(`Plugin ${config.transform}: invalid plugin format, expected { transform: Function }`);
74
+ return;
90
75
  }
76
+ plugins.push(plugin);
91
77
  }));
92
78
  }
93
- return Promise.all(promises).then(() => ({ after, before }));
79
+ await Promise.all(promises);
80
+ return plugins;
94
81
  }
95
82
  function main() {
96
83
  let tsconfig = ts.findConfigFile(process.cwd(), ts.sys.fileExists, 'tsconfig.json');
@@ -101,9 +88,12 @@ function main() {
101
88
  if (error) {
102
89
  return passthrough();
103
90
  }
104
- let plugins = config?.compilerOptions?.plugins?.filter((p) => typeof p === 'object' && p !== null && 'transform' in p) ?? [];
105
- console.log(`Found ${plugins.length} transformer plugin(s), using programmatic build...`);
106
- build(config, tsconfig, plugins).catch((err) => {
91
+ let pluginConfigs = config?.compilerOptions?.plugins?.filter((p) => typeof p === 'object' && p !== null && 'transform' in p) ?? [];
92
+ if (pluginConfigs.length === 0) {
93
+ return passthrough();
94
+ }
95
+ console.log(`Found ${pluginConfigs.length} transformer plugin(s), using coordinated build...`);
96
+ build(config, tsconfig, pluginConfigs).catch((err) => {
107
97
  console.error(err);
108
98
  process.exit(1);
109
99
  });
@@ -1,6 +1,4 @@
1
- import { ts } from '../../index.js';
1
+ import ts from 'typescript';
2
2
  declare const getExpressionName: (node: ts.Expression) => string | null;
3
- declare const getPropertyPath: (node: ts.Expression) => string[] | null;
4
- declare const getPropertyPathString: (node: ts.Expression) => string | null;
5
- declare const unwrapParentheses: (expr: ts.Expression) => ts.Expression;
6
- export { getExpressionName, getPropertyPath, getPropertyPathString, unwrapParentheses };
3
+ declare const getPropertyPath: (node: ts.Expression) => string | null;
4
+ export { getExpressionName, getPropertyPath };
@@ -1,10 +1,10 @@
1
- import { ts } from '../../index.js';
1
+ import ts from 'typescript';
2
2
  const getExpressionName = (node) => {
3
3
  if (ts.isIdentifier(node)) {
4
4
  return node.text;
5
5
  }
6
6
  if (ts.isPropertyAccessExpression(node)) {
7
- return getPropertyPathString(node);
7
+ return getPropertyPath(node);
8
8
  }
9
9
  return null;
10
10
  };
@@ -16,18 +16,8 @@ const getPropertyPath = (node) => {
16
16
  }
17
17
  if (ts.isIdentifier(current)) {
18
18
  parts.push(current.text);
19
- return parts.reverse();
19
+ return parts.reverse().join('.');
20
20
  }
21
21
  return null;
22
22
  };
23
- const getPropertyPathString = (node) => {
24
- let parts = getPropertyPath(node);
25
- return parts ? parts.join('.') : null;
26
- };
27
- const unwrapParentheses = (expr) => {
28
- while (ts.isParenthesizedExpression(expr)) {
29
- expr = expr.expression;
30
- }
31
- return expr;
32
- };
33
- export { getExpressionName, getPropertyPath, getPropertyPathString, unwrapParentheses };
23
+ export { getExpressionName, getPropertyPath };
@@ -2,7 +2,6 @@ type Range = {
2
2
  end: number;
3
3
  start: number;
4
4
  };
5
- declare const containsPosition: (range: Range, pos: number) => boolean;
6
5
  declare const inRange: (ranges: Range[], start: number, end: number) => boolean;
7
- export { containsPosition, inRange };
6
+ export { inRange };
8
7
  export type { Range };
@@ -1,6 +1,3 @@
1
- const containsPosition = (range, pos) => {
2
- return pos >= range.start && pos <= range.end;
3
- };
4
1
  const inRange = (ranges, start, end) => {
5
2
  for (let i = 0, n = ranges.length; i < n; i++) {
6
3
  let r = ranges[i];
@@ -10,4 +7,4 @@ const inRange = (ranges, start, end) => {
10
7
  }
11
8
  return false;
12
9
  };
13
- export { containsPosition, inRange };
10
+ export { inRange };
@@ -1,7 +1,5 @@
1
- import type { QuickCheckPattern, Replacement } from './types.js';
1
+ import type { Replacement } from './types.js';
2
2
  declare const _default: {
3
- contains: (code: string, { regex, patterns }: QuickCheckPattern) => boolean;
4
- replace: (code: string, replacements: Replacement[]) => string;
5
3
  replaceReverse: (code: string, replacements: Replacement[]) => string;
6
4
  };
7
5
  export default _default;
@@ -1,35 +1,3 @@
1
- const contains = (code, { regex, patterns }) => {
2
- if (regex) {
3
- return regex.test(code);
4
- }
5
- if (patterns) {
6
- for (let i = 0, n = patterns.length; i < n; i++) {
7
- if (code.indexOf(patterns[i]) !== -1) {
8
- return true;
9
- }
10
- }
11
- }
12
- return false;
13
- };
14
- const replace = (code, replacements) => {
15
- if (replacements.length === 0) {
16
- return code;
17
- }
18
- replacements.sort((a, b) => a.start - b.start);
19
- let parts = [], pos = 0;
20
- for (let i = 0, n = replacements.length; i < n; i++) {
21
- let r = replacements[i];
22
- if (r.start > pos) {
23
- parts.push(code.substring(pos, r.start));
24
- }
25
- parts.push(r.newText);
26
- pos = r.end;
27
- }
28
- if (pos < code.length) {
29
- parts.push(code.substring(pos));
30
- }
31
- return parts.join('');
32
- };
33
1
  const replaceReverse = (code, replacements) => {
34
2
  if (replacements.length === 0) {
35
3
  return code;
@@ -42,4 +10,4 @@ const replaceReverse = (code, replacements) => {
42
10
  }
43
11
  return result;
44
12
  };
45
- export default { contains, replace, replaceReverse };
13
+ export default { replaceReverse };
@@ -0,0 +1,12 @@
1
+ import type { Plugin, SharedContext } from './types.js';
2
+ import { ts } from '../index.js';
3
+ type CoordinatorResult = {
4
+ changed: boolean;
5
+ code: string;
6
+ sourceFile: ts.SourceFile;
7
+ };
8
+ declare const _default: {
9
+ transform: (plugins: Plugin[], sourceCode: string, sourceFile: ts.SourceFile, program: ts.Program, shared: SharedContext) => CoordinatorResult;
10
+ };
11
+ export default _default;
12
+ export type { CoordinatorResult };
@@ -0,0 +1,100 @@
1
+ import { ts } from '../index.js';
2
+ import code from './code.js';
3
+ import imports from './imports.js';
4
+ function applyImports(sourceCode, sourceFile, intents) {
5
+ let result = sourceCode;
6
+ for (let i = 0, n = intents.length; i < n; i++) {
7
+ let intent = intents[i];
8
+ result = imports.modify(result, sourceFile, intent.package, {
9
+ add: intent.add,
10
+ namespace: intent.namespace,
11
+ remove: intent.remove
12
+ });
13
+ if (i < n - 1) {
14
+ sourceFile = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
15
+ }
16
+ }
17
+ return result;
18
+ }
19
+ function applyIntents(sourceCode, sourceFile, intents) {
20
+ if (intents.length === 0) {
21
+ return sourceCode;
22
+ }
23
+ let replacements = intents.map(intent => ({
24
+ end: intent.node.end,
25
+ newText: intent.generate(sourceFile),
26
+ start: intent.node.getStart(sourceFile)
27
+ }));
28
+ return code.replaceReverse(sourceCode, replacements);
29
+ }
30
+ function applyPrepend(sourceCode, sourceFile, prepend) {
31
+ if (prepend.length === 0) {
32
+ return sourceCode;
33
+ }
34
+ let insertPos = findLastImportEnd(sourceFile), prependText = prepend.join('\n') + '\n';
35
+ if (insertPos === 0) {
36
+ return prependText + sourceCode;
37
+ }
38
+ return sourceCode.slice(0, insertPos) + '\n' + prependText + sourceCode.slice(insertPos);
39
+ }
40
+ function findLastImportEnd(sourceFile) {
41
+ let lastEnd = 0;
42
+ for (let i = 0, n = sourceFile.statements.length; i < n; i++) {
43
+ let stmt = sourceFile.statements[i];
44
+ if (ts.isImportDeclaration(stmt)) {
45
+ lastEnd = stmt.end;
46
+ }
47
+ else {
48
+ break;
49
+ }
50
+ }
51
+ return lastEnd;
52
+ }
53
+ function hasPattern(sourceCode, patterns) {
54
+ for (let i = 0, n = patterns.length; i < n; i++) {
55
+ if (sourceCode.indexOf(patterns[i]) !== -1) {
56
+ return true;
57
+ }
58
+ }
59
+ return false;
60
+ }
61
+ const transform = (plugins, sourceCode, sourceFile, program, shared) => {
62
+ if (plugins.length === 0) {
63
+ return { changed: false, code: sourceCode, sourceFile };
64
+ }
65
+ let changed = false, currentCode = sourceCode, currentSourceFile = sourceFile;
66
+ for (let i = 0, n = plugins.length; i < n; i++) {
67
+ let plugin = plugins[i];
68
+ if (plugin.patterns && !hasPattern(currentCode, plugin.patterns)) {
69
+ continue;
70
+ }
71
+ let result = plugin.transform({
72
+ checker: program.getTypeChecker(),
73
+ code: currentCode,
74
+ program,
75
+ shared,
76
+ sourceFile: currentSourceFile
77
+ });
78
+ let hasChanges = (result.imports && result.imports.length > 0) ||
79
+ (result.prepend && result.prepend.length > 0) ||
80
+ (result.replacements && result.replacements.length > 0);
81
+ if (!hasChanges) {
82
+ continue;
83
+ }
84
+ changed = true;
85
+ if (result.replacements && result.replacements.length > 0) {
86
+ currentCode = applyIntents(currentCode, currentSourceFile, result.replacements);
87
+ currentSourceFile = ts.createSourceFile(currentSourceFile.fileName, currentCode, currentSourceFile.languageVersion, true);
88
+ }
89
+ if (result.prepend && result.prepend.length > 0) {
90
+ currentCode = applyPrepend(currentCode, currentSourceFile, result.prepend);
91
+ currentSourceFile = ts.createSourceFile(currentSourceFile.fileName, currentCode, currentSourceFile.languageVersion, true);
92
+ }
93
+ if (result.imports && result.imports.length > 0) {
94
+ currentCode = applyImports(currentCode, currentSourceFile, result.imports);
95
+ currentSourceFile = ts.createSourceFile(currentSourceFile.fileName, currentCode, currentSourceFile.languageVersion, true);
96
+ }
97
+ }
98
+ return { changed, code: currentCode, sourceFile: currentSourceFile };
99
+ };
100
+ export default { transform };
@@ -12,6 +12,7 @@ type ModifyOptions = {
12
12
  declare const _default: {
13
13
  find: (sourceFile: ts.SourceFile, packageName: string) => ImportInfo[];
14
14
  isFromPackage: (node: ts.Identifier, packageName: string, checker?: ts.TypeChecker) => boolean;
15
+ isSymbolFromPackage: (checker: ts.TypeChecker, node: ts.Node, packageName: string, symbolName?: string) => boolean;
15
16
  modify: (sourceCode: string, sourceFile: ts.SourceFile, packageName: string, options: ModifyOptions) => string;
16
17
  trace: (node: ts.Identifier, checker: ts.TypeChecker) => string | null;
17
18
  };
@@ -29,6 +29,28 @@ const isFromPackage = (node, packageName, checker) => {
29
29
  let origin = trace(node, checker);
30
30
  return origin === null || origin.includes(packageName);
31
31
  };
32
+ const isSymbolFromPackage = (checker, node, packageName, symbolName) => {
33
+ let symbol = checker.getSymbolAtLocation(node);
34
+ if (!symbol) {
35
+ return false;
36
+ }
37
+ if (symbol.flags & ts.SymbolFlags.Alias) {
38
+ symbol = checker.getAliasedSymbol(symbol);
39
+ }
40
+ if (symbolName && symbol.name !== symbolName) {
41
+ return false;
42
+ }
43
+ let declarations = symbol.getDeclarations();
44
+ if (!declarations || declarations.length === 0) {
45
+ return false;
46
+ }
47
+ for (let i = 0, n = declarations.length; i < n; i++) {
48
+ if (declarations[i].getSourceFile().fileName.includes(packageName)) {
49
+ return true;
50
+ }
51
+ }
52
+ return false;
53
+ };
32
54
  const modify = (sourceCode, sourceFile, packageName, options) => {
33
55
  let { namespace } = options;
34
56
  if (!options.add && !options.namespace && !options.remove) {
@@ -92,4 +114,4 @@ const trace = (node, checker) => {
92
114
  }
93
115
  return declarations[0].getSourceFile().fileName;
94
116
  };
95
- export default { find, isFromPackage, modify, trace };
117
+ export default { find, isFromPackage, isSymbolFromPackage, modify, trace };
@@ -1,7 +1,7 @@
1
1
  export * as ast from './ast/index.js';
2
2
  export { default as code } from './code.js';
3
+ export { default as coordinator } from './coordinator.js';
3
4
  export { default as imports } from './imports.js';
4
5
  export { default as plugin } from './plugins/index.js';
5
- export { default as program } from './program.js';
6
6
  export { default as uid } from './uid.js';
7
7
  export type * from './types.js';
@@ -1,6 +1,6 @@
1
1
  export * as ast from './ast/index.js';
2
2
  export { default as code } from './code.js';
3
+ export { default as coordinator } from './coordinator.js';
3
4
  export { default as imports } from './imports.js';
4
5
  export { default as plugin } from './plugins/index.js';
5
- export { default as program } from './program.js';
6
6
  export { default as uid } from './uid.js';
@@ -1,13 +1,11 @@
1
1
  declare const _default: {
2
- tsc: ({ analyze, transform }: import("../index.js").PluginDefinition) => (program: import("typescript").Program, context: import("../index.js").PluginContext) => {
3
- analyze: ((sourceFile: import("typescript").SourceFile) => void) | undefined;
2
+ tsc: (plugins: import("../index.js").Plugin[]) => (program: import("typescript").Program, shared: import("../index.js").SharedContext) => {
4
3
  transform: import("typescript").TransformerFactory<import("typescript").SourceFile>;
5
4
  };
6
- vite: ({ analyze, name, onWatchChange, transform }: {
7
- analyze?: import("../index.js").AnalyzeFn;
5
+ vite: ({ name, onWatchChange, plugins }: {
8
6
  name: string;
9
7
  onWatchChange?: () => void;
10
- transform: import("../index.js").TransformFn;
8
+ plugins: import("../index.js").Plugin[];
11
9
  }) => ({ root }?: {
12
10
  root?: string;
13
11
  }) => {
@@ -1,7 +1,6 @@
1
- import type { PluginContext, PluginDefinition } from '../types.js';
2
- import { ts } from '../../index.js';
3
- declare const _default: ({ analyze, transform }: PluginDefinition) => (program: ts.Program, context: PluginContext) => {
4
- analyze: ((sourceFile: ts.SourceFile) => void) | undefined;
1
+ import type { Plugin, SharedContext } from '../types.js';
2
+ import type ts from 'typescript';
3
+ declare const _default: (plugins: Plugin[]) => (program: ts.Program, shared: SharedContext) => {
5
4
  transform: ts.TransformerFactory<ts.SourceFile>;
6
5
  };
7
6
  export default _default;
@@ -1,12 +1,10 @@
1
- export default ({ analyze, transform }) => {
2
- return (program, context) => {
1
+ import coordinator from '../coordinator.js';
2
+ export default (plugins) => {
3
+ return (program, shared) => {
3
4
  return {
4
- analyze: analyze
5
- ? (sourceFile) => analyze(sourceFile, program, context)
6
- : undefined,
7
5
  transform: (() => {
8
6
  return (sourceFile) => {
9
- let result = transform(sourceFile, program, context);
7
+ let result = coordinator.transform(plugins, sourceFile.getFullText(), sourceFile, program, shared);
10
8
  return result.changed ? result.sourceFile : sourceFile;
11
9
  };
12
10
  })
@@ -1,4 +1,4 @@
1
- import type { AnalyzeFn, TransformFn } from '../types.js';
1
+ import type { Plugin } from '../types.js';
2
2
  type VitePlugin = {
3
3
  configResolved: (config: unknown) => void;
4
4
  enforce: 'pre';
@@ -10,12 +10,11 @@ type VitePlugin = {
10
10
  watchChange: (id: string) => void;
11
11
  };
12
12
  type VitePluginOptions = {
13
- analyze?: AnalyzeFn;
14
13
  name: string;
15
14
  onWatchChange?: () => void;
16
- transform: TransformFn;
15
+ plugins: Plugin[];
17
16
  };
18
- declare const _default: ({ analyze, name, onWatchChange, transform }: VitePluginOptions) => ({ root }?: {
17
+ declare const _default: ({ name, onWatchChange, plugins }: VitePluginOptions) => ({ root }?: {
19
18
  root?: string;
20
19
  }) => VitePlugin;
21
20
  export default _default;
@@ -1,8 +1,9 @@
1
1
  import { ts } from '../../index.js';
2
+ import coordinator from '../coordinator.js';
2
3
  import program from '../program.js';
3
4
  const FILE_REGEX = /\.[tj]sx?$/;
4
5
  let contexts = new Map();
5
- export default ({ analyze, name, onWatchChange, transform }) => {
6
+ export default ({ name, onWatchChange, plugins }) => {
6
7
  return ({ root } = {}) => {
7
8
  return {
8
9
  configResolved(config) {
@@ -15,9 +16,8 @@ export default ({ analyze, name, onWatchChange, transform }) => {
15
16
  return null;
16
17
  }
17
18
  try {
18
- let context = contexts.get(root || '') ?? contexts.set(root || '', new Map()).get(root || ''), prog = program.get(root || ''), sourceFile = ts.createSourceFile(id, code, ts.ScriptTarget.Latest, true);
19
- analyze?.(sourceFile, prog, context);
20
- let result = transform(sourceFile, prog, context);
19
+ let prog = program.get(root || ''), shared = contexts.get(root || '') ?? contexts.set(root || '', new Map()).get(root || ''), sourceFile = ts.createSourceFile(id, code, ts.ScriptTarget.Latest, true);
20
+ let result = coordinator.transform(plugins, code, sourceFile, prog, shared);
21
21
  if (!result.changed) {
22
22
  return null;
23
23
  }
@@ -1,14 +1,15 @@
1
1
  import type ts from 'typescript';
2
- type AnalyzeFn = (sourceFile: ts.SourceFile, program: ts.Program, context: PluginContext) => void;
3
- type PluginContext = Map<string, unknown>;
4
- type PluginDefinition = {
5
- analyze?: AnalyzeFn;
6
- transform: TransformFn;
2
+ type ImportIntent = {
3
+ add?: string[];
4
+ namespace?: string;
5
+ package: string;
6
+ remove?: string[];
7
7
  };
8
- type QuickCheckPattern = {
8
+ type Plugin = {
9
9
  patterns?: string[];
10
- regex?: RegExp;
10
+ transform: (ctx: TransformContext) => TransformResult;
11
11
  };
12
+ type PluginFactory = (options?: Record<string, unknown>) => Plugin;
12
13
  type Range = {
13
14
  end: number;
14
15
  start: number;
@@ -16,10 +17,21 @@ type Range = {
16
17
  type Replacement = Range & {
17
18
  newText: string;
18
19
  };
19
- type TransformFn = (sourceFile: ts.SourceFile, program: ts.Program, context?: PluginContext) => TransformResult;
20
- type TransformResult = {
21
- changed: boolean;
20
+ type ReplacementIntent = {
21
+ generate: (sourceFile: ts.SourceFile) => string;
22
+ node: ts.Node;
23
+ };
24
+ type SharedContext = Map<string, unknown>;
25
+ type TransformContext = {
26
+ checker: ts.TypeChecker;
22
27
  code: string;
28
+ program: ts.Program;
29
+ shared: SharedContext;
23
30
  sourceFile: ts.SourceFile;
24
31
  };
25
- export type { AnalyzeFn, PluginContext, PluginDefinition, QuickCheckPattern, Range, Replacement, TransformFn, TransformResult };
32
+ type TransformResult = {
33
+ imports?: ImportIntent[];
34
+ prepend?: string[];
35
+ replacements?: ReplacementIntent[];
36
+ };
37
+ export type { ImportIntent, Plugin, PluginFactory, Range, Replacement, ReplacementIntent, SharedContext, TransformContext, TransformResult };
package/package.json CHANGED
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "type": "module",
39
39
  "types": "build/index.d.ts",
40
- "version": "0.24.2",
40
+ "version": "0.25.1",
41
41
  "scripts": {
42
42
  "build": "tsc && tsc-alias",
43
43
  "-": "-"