@teambit/typescript 1.0.106 → 1.0.108
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/compiler-options.ts +27 -0
- package/dedupe-path.spec.ts +34 -0
- package/dist/cmds/write-tsconfig.cmd.d.ts +2 -2
- package/dist/compiler-options.d.ts +2 -2
- package/dist/extractor-options.d.ts +1 -1
- package/dist/identifier-list.d.ts +1 -1
- package/dist/identifier.d.ts +3 -3
- package/dist/{preview-1703505948637.js → preview-1703647408454.js} +2 -2
- package/dist/schema-extractor-context.d.ts +11 -11
- package/dist/schema-extractor-context.js +13 -17
- package/dist/schema-extractor-context.js.map +1 -1
- package/dist/schema-transformer.d.ts +2 -2
- package/dist/sourceFileTransformers/export.js +1 -2
- package/dist/sourceFileTransformers/export.js.map +1 -1
- package/dist/sourceFileTransformers/function.js +2 -3
- package/dist/sourceFileTransformers/function.js.map +1 -1
- package/dist/sourceFileTransformers/import.js +1 -2
- package/dist/sourceFileTransformers/import.js.map +1 -1
- package/dist/sourceFileTransformers/index.d.ts +1 -1
- package/dist/sourceFileTransformers/transform.js +1 -1
- package/dist/sourceFileTransformers/transform.js.map +1 -1
- package/dist/transformers/binding-element.js +1 -2
- package/dist/transformers/binding-element.js.map +1 -1
- package/dist/transformers/class-declaration.js +3 -6
- package/dist/transformers/class-declaration.js.map +1 -1
- package/dist/transformers/constructor.js +3 -4
- package/dist/transformers/constructor.js.map +1 -1
- package/dist/transformers/enum-declaration.js +1 -2
- package/dist/transformers/enum-declaration.js.map +1 -1
- package/dist/transformers/export-declaration.js +5 -7
- package/dist/transformers/export-declaration.js.map +1 -1
- package/dist/transformers/function-like.js +5 -8
- package/dist/transformers/function-like.js.map +1 -1
- package/dist/transformers/get-accessor.js +1 -2
- package/dist/transformers/get-accessor.js.map +1 -1
- package/dist/transformers/import-declaration.js +2 -3
- package/dist/transformers/import-declaration.js.map +1 -1
- package/dist/transformers/interface-declaration.js +1 -2
- package/dist/transformers/interface-declaration.js.map +1 -1
- package/dist/transformers/parameter.js +1 -2
- package/dist/transformers/parameter.js.map +1 -1
- package/dist/transformers/property-declaration.js +1 -2
- package/dist/transformers/property-declaration.js.map +1 -1
- package/dist/transformers/source-file-transformer.js +3 -5
- package/dist/transformers/source-file-transformer.js.map +1 -1
- package/dist/transformers/utils/jsdoc-to-doc-schema.js +2 -4
- package/dist/transformers/utils/jsdoc-to-doc-schema.js.map +1 -1
- package/dist/transformers/utils/parse-type-from-quick-info.js +1 -2
- package/dist/transformers/utils/parse-type-from-quick-info.js.map +1 -1
- package/dist/transformers/variable-declaration.js +8 -12
- package/dist/transformers/variable-declaration.js.map +1 -1
- package/dist/tsconfig-writer.d.ts +2 -2
- package/dist/tsconfig-writer.js +1 -4
- package/dist/tsconfig-writer.js.map +1 -1
- package/dist/typescript.composition.d.ts +2 -2
- package/dist/typescript.extractor.d.ts +3 -3
- package/dist/typescript.extractor.js +5 -7
- package/dist/typescript.extractor.js.map +1 -1
- package/dist/typescript.main.runtime.d.ts +7 -7
- package/dist/typescript.main.runtime.js +1 -2
- package/dist/typescript.main.runtime.js.map +1 -1
- package/dist/typescript.parser.js +7 -8
- package/dist/typescript.parser.js.map +1 -1
- package/dist/typescript.parser.spec.js +12 -15
- package/dist/typescript.parser.spec.js.map +1 -1
- package/export-identifier.ts +14 -0
- package/extractor-options.ts +21 -0
- package/identifier-list.ts +14 -0
- package/identifier.ts +23 -0
- package/index.ts +14 -0
- package/package.json +27 -34
- package/remove-types-task.ts +33 -0
- package/schema-extractor-context.ts +625 -0
- package/schema-transformer.plugin.ts +14 -0
- package/schema-transformer.ts +23 -0
- package/transform-source-file.spec.ts +155 -0
- package/tsconfig-writer.ts +249 -0
- package/tsconfig.json +16 -21
- package/types/asset.d.ts +15 -3
- package/typescript.aspect.ts +5 -0
- package/typescript.compiler.spec.ts +63 -0
- package/typescript.compiler.ts +286 -0
- package/typescript.extractor.ts +247 -0
- package/typescript.main.runtime.ts +442 -0
- package/typescript.parser.spec.ts +221 -0
- package/typescript.parser.ts +123 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import {
|
|
3
|
+
transformSourceFile,
|
|
4
|
+
classNamesTransformer,
|
|
5
|
+
functionNamesTransformer,
|
|
6
|
+
importTransformer,
|
|
7
|
+
interfaceNamesTransformer,
|
|
8
|
+
typeAliasNamesTransformer,
|
|
9
|
+
variableNamesTransformer,
|
|
10
|
+
expressionStatementTransformer,
|
|
11
|
+
exportTransformer,
|
|
12
|
+
} from './sourceFileTransformers';
|
|
13
|
+
|
|
14
|
+
function normalizeFormatting(code: string): string {
|
|
15
|
+
const sourceFile = ts.createSourceFile('temp.ts', code, ts.ScriptTarget.Latest, true);
|
|
16
|
+
const printer = ts.createPrinter({
|
|
17
|
+
newLine: ts.NewLineKind.LineFeed,
|
|
18
|
+
removeComments: false,
|
|
19
|
+
});
|
|
20
|
+
return printer.printFile(sourceFile);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('transformSourceFile', () => {
|
|
24
|
+
const createTransformerTest = (
|
|
25
|
+
sourceCode: string,
|
|
26
|
+
nameMapping: any,
|
|
27
|
+
transformer: (nameMapping: any) => ts.TransformerFactory<ts.SourceFile>,
|
|
28
|
+
expectedCode: string
|
|
29
|
+
) => {
|
|
30
|
+
it(`should correctly transform source code with ${transformer.name}`, async () => {
|
|
31
|
+
const result = await transformSourceFile('test.ts', sourceCode, [transformer(nameMapping)]);
|
|
32
|
+
const normalizedResult = normalizeFormatting(result);
|
|
33
|
+
const normalizedExpectedCode = normalizeFormatting(expectedCode);
|
|
34
|
+
|
|
35
|
+
expect(normalizedResult).toBe(normalizedExpectedCode);
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const testCases = [
|
|
40
|
+
{
|
|
41
|
+
transformer: classNamesTransformer,
|
|
42
|
+
sourceCode: 'class TestClass {}',
|
|
43
|
+
nameMapping: { TestClass: 'NewClassName' },
|
|
44
|
+
expectedCode: 'class NewClassName {}',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
transformer: functionNamesTransformer,
|
|
48
|
+
sourceCode: 'function testFunction() {}',
|
|
49
|
+
nameMapping: { testFunction: 'newFunctionName' },
|
|
50
|
+
expectedCode: 'function newFunctionName() {}',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
transformer: importTransformer,
|
|
54
|
+
sourceCode: 'import { Test } from "./test";',
|
|
55
|
+
nameMapping: { './test': './newTest' },
|
|
56
|
+
expectedCode: 'import { Test } from "./newTest";',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
transformer: interfaceNamesTransformer,
|
|
60
|
+
sourceCode: 'interface TestInterface {}',
|
|
61
|
+
nameMapping: { TestInterface: 'NewInterfaceName' },
|
|
62
|
+
expectedCode: 'interface NewInterfaceName {}',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
transformer: typeAliasNamesTransformer,
|
|
66
|
+
sourceCode: 'type TestType = string;',
|
|
67
|
+
nameMapping: { TestType: 'NewTypeName' },
|
|
68
|
+
expectedCode: 'type NewTypeName = string;',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
transformer: variableNamesTransformer,
|
|
72
|
+
sourceCode: 'let testVariable = "test";',
|
|
73
|
+
nameMapping: { testVariable: 'newVariableName' },
|
|
74
|
+
expectedCode: 'let newVariableName = "test";',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
transformer: classNamesTransformer,
|
|
78
|
+
sourceCode: 'class TestClass { oldMethod() {} }',
|
|
79
|
+
nameMapping: { TestClass: 'NewClassName', oldMethod: 'newMethodName' },
|
|
80
|
+
expectedCode: 'class NewClassName { newMethodName() {} }',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
transformer: classNamesTransformer,
|
|
84
|
+
sourceCode: 'class TestClass { oldMember: string; }',
|
|
85
|
+
nameMapping: { TestClass: 'NewClassName', oldMember: 'newMember' },
|
|
86
|
+
expectedCode: 'class NewClassName { newMember: string; }',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
transformer: expressionStatementTransformer,
|
|
90
|
+
sourceCode: 'TestClass.staticMethod();',
|
|
91
|
+
nameMapping: { TestClass: 'NewClassName', staticMethod: 'newMethodName' },
|
|
92
|
+
expectedCode: 'NewClassName.newMethodName();',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
transformer: expressionStatementTransformer,
|
|
96
|
+
sourceCode: 'let instance = new TestClass(); instance.method();',
|
|
97
|
+
nameMapping: { TestClass: 'NewClassName', method: 'newMethodName' },
|
|
98
|
+
expectedCode: 'let instance = new NewClassName(); instance.newMethodName();',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
transformer: interfaceNamesTransformer,
|
|
102
|
+
sourceCode: 'interface TestInterface { oldMember: string; }',
|
|
103
|
+
nameMapping: { TestInterface: 'NewInterfaceName', oldMember: 'newMember' },
|
|
104
|
+
expectedCode: 'interface NewInterfaceName { newMember: string; }',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
transformer: importTransformer,
|
|
108
|
+
sourceCode: 'const UI = require("@xxx/ui-library");',
|
|
109
|
+
nameMapping: { '@xxx/ui-library': '@abc/ui-library' },
|
|
110
|
+
expectedCode: 'const UI = require("@abc/ui-library");',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
transformer: exportTransformer,
|
|
114
|
+
sourceCode: 'export { Component } from "@xxx/ui-library";',
|
|
115
|
+
nameMapping: { '@xxx/ui-library': '@abc/ui-library' },
|
|
116
|
+
expectedCode: 'export { Component } from "@abc/ui-library";',
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
transformer: exportTransformer,
|
|
120
|
+
sourceCode: 'export * from "@xxx/ui-library";',
|
|
121
|
+
nameMapping: { '@xxx/ui-library': '@abc/ui-library' },
|
|
122
|
+
expectedCode: 'export * from "@abc/ui-library";',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
transformer: exportTransformer,
|
|
126
|
+
sourceCode: 'export { default as UI } from "@xxx/ui-library";',
|
|
127
|
+
nameMapping: { '@xxx/ui-library': '@abc/ui-library' },
|
|
128
|
+
expectedCode: 'export { default as UI } from "@abc/ui-library";',
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
for (const { transformer, sourceCode, nameMapping, expectedCode } of testCases) {
|
|
133
|
+
createTransformerTest(sourceCode, nameMapping, transformer, expectedCode);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
it('should return original source code if no transformations are applied', async () => {
|
|
137
|
+
const sourceCode = 'class TestClass {} function testFunction() {}';
|
|
138
|
+
const result = await transformSourceFile('test.ts', sourceCode, []);
|
|
139
|
+
const normalizedResult = normalizeFormatting(result);
|
|
140
|
+
const normalizedSourceCode = normalizeFormatting(sourceCode);
|
|
141
|
+
expect(normalizedResult).toBe(normalizedSourceCode);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should handle multiple transformers', async () => {
|
|
145
|
+
const sourceCode = 'class TestClass {} function testFunction() {}';
|
|
146
|
+
const expectedCode = 'class NewClassName {} function newFunctionName() {}';
|
|
147
|
+
const result = await transformSourceFile('test.ts', sourceCode, [
|
|
148
|
+
classNamesTransformer({ TestClass: 'NewClassName' }),
|
|
149
|
+
functionNamesTransformer({ testFunction: 'newFunctionName' }),
|
|
150
|
+
]);
|
|
151
|
+
const normalizedResult = normalizeFormatting(result);
|
|
152
|
+
const normalizedExpectedCode = normalizeFormatting(expectedCode);
|
|
153
|
+
expect(normalizedResult).toBe(normalizedExpectedCode);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import yesno from 'yesno';
|
|
4
|
+
import { PathLinuxRelative } from '@teambit/legacy/dist/utils/path';
|
|
5
|
+
import { compact, invertBy, isEqual, uniq } from 'lodash';
|
|
6
|
+
import { PromptCanceled } from '@teambit/legacy/dist/prompts/exceptions';
|
|
7
|
+
import { Environment, ExecutionContext } from '@teambit/envs';
|
|
8
|
+
import { Workspace } from '@teambit/workspace';
|
|
9
|
+
import { Logger } from '@teambit/logger';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import { TsconfigWriterOptions } from './typescript.main.runtime';
|
|
12
|
+
import { TypescriptAspect } from '.';
|
|
13
|
+
|
|
14
|
+
export type TsconfigPathsPerEnv = { envIds: string[]; tsconfig: string; paths: string[] };
|
|
15
|
+
|
|
16
|
+
type PathsPerEnv = { env: Environment; ids: string[]; paths: string[] };
|
|
17
|
+
|
|
18
|
+
export class TsconfigWriter {
|
|
19
|
+
constructor(private workspace: Workspace, private logger: Logger) {}
|
|
20
|
+
|
|
21
|
+
async write(
|
|
22
|
+
envsExecutionContext: ExecutionContext[],
|
|
23
|
+
options: TsconfigWriterOptions
|
|
24
|
+
): Promise<TsconfigPathsPerEnv[]> {
|
|
25
|
+
const pathsPerEnvs = this.getPathsPerEnv(envsExecutionContext, options);
|
|
26
|
+
const tsconfigPathsPerEnv = pathsPerEnvs.map((pathsPerEnv) => ({
|
|
27
|
+
envIds: pathsPerEnv.ids,
|
|
28
|
+
tsconfig: this.getTsconfigFromEnv(pathsPerEnv.env), // pathsPerEnv.env.getTsConfig(),
|
|
29
|
+
paths: pathsPerEnv.paths,
|
|
30
|
+
}));
|
|
31
|
+
if (options.dryRun) return tsconfigPathsPerEnv;
|
|
32
|
+
if (!options.silent) await this.promptForWriting(tsconfigPathsPerEnv.map((p) => p.paths).flat());
|
|
33
|
+
await this.writeFiles(tsconfigPathsPerEnv);
|
|
34
|
+
return tsconfigPathsPerEnv;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private getTsconfigFromEnv(env: Environment) {
|
|
38
|
+
const compiler = env.getCompiler();
|
|
39
|
+
if (compiler.id === TypescriptAspect.id) {
|
|
40
|
+
return compiler.displayConfig() as string;
|
|
41
|
+
}
|
|
42
|
+
return JSON.stringify(env.getTsConfig(), undefined, 2);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async clean(envsExecutionContext: ExecutionContext[], { dryRun, silent }: TsconfigWriterOptions): Promise<string[]> {
|
|
46
|
+
const pathsPerEnvs = this.getPathsPerEnv(envsExecutionContext, { dedupe: false });
|
|
47
|
+
const componentPaths = pathsPerEnvs.map((p) => p.paths).flat();
|
|
48
|
+
const allPossibleDirs = getAllPossibleDirsFromPaths(componentPaths);
|
|
49
|
+
const dirsWithTsconfig = await filterDirsWithTsconfigFile(allPossibleDirs);
|
|
50
|
+
const tsconfigFiles = dirsWithTsconfig.map((dir) => path.join(dir, 'tsconfig.json'));
|
|
51
|
+
if (dryRun) return tsconfigFiles;
|
|
52
|
+
if (!dirsWithTsconfig.length) return [];
|
|
53
|
+
if (!silent) await this.promptForCleaning(tsconfigFiles);
|
|
54
|
+
await this.deleteFiles(tsconfigFiles);
|
|
55
|
+
return tsconfigFiles;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private async promptForWriting(dirs: string[]) {
|
|
59
|
+
this.logger.clearStatusLine();
|
|
60
|
+
const tsconfigFiles = dirs.map((dir) => path.join(dir, 'tsconfig.json'));
|
|
61
|
+
const ok = await yesno({
|
|
62
|
+
question: `${chalk.underline('The following paths will be written:')}
|
|
63
|
+
${tsconfigFiles.join('\n')}
|
|
64
|
+
${chalk.bold('Do you want to continue? [yes(y)/no(n)]')}`,
|
|
65
|
+
});
|
|
66
|
+
if (!ok) {
|
|
67
|
+
throw new PromptCanceled();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private async promptForCleaning(tsconfigFiles: string[]) {
|
|
72
|
+
this.logger.clearStatusLine();
|
|
73
|
+
const ok = await yesno({
|
|
74
|
+
question: `${chalk.underline('The following paths will be deleted:')}
|
|
75
|
+
${tsconfigFiles.join('\n')}
|
|
76
|
+
${chalk.bold('Do you want to continue? [yes(y)/no(n)]')}`,
|
|
77
|
+
});
|
|
78
|
+
if (!ok) {
|
|
79
|
+
throw new PromptCanceled();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private async deleteFiles(tsconfigFiles: string[]) {
|
|
84
|
+
await Promise.all(tsconfigFiles.map((f) => fs.remove(f)));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private async writeFiles(tsconfigPathsPerEnvs: TsconfigPathsPerEnv[]) {
|
|
88
|
+
await Promise.all(
|
|
89
|
+
tsconfigPathsPerEnvs.map((pathsPerEnv) => {
|
|
90
|
+
return Promise.all(
|
|
91
|
+
pathsPerEnv.paths.map((p) => fs.writeFile(path.join(p, 'tsconfig.json'), pathsPerEnv.tsconfig))
|
|
92
|
+
);
|
|
93
|
+
})
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private getPathsPerEnv(envsExecutionContext: ExecutionContext[], { dedupe }: TsconfigWriterOptions): PathsPerEnv[] {
|
|
98
|
+
const pathsPerEnvs = envsExecutionContext.map((envExecution) => {
|
|
99
|
+
return {
|
|
100
|
+
id: envExecution.id,
|
|
101
|
+
env: envExecution.env,
|
|
102
|
+
paths: envExecution.components.map((c) => this.workspace.componentDir(c.id, undefined, { relative: true })),
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
if (!dedupe) {
|
|
106
|
+
return pathsPerEnvs.map(({ id, env, paths }) => ({ ids: [id], env, paths }));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const envsWithFiles = envsExecutionContext.map((e) => ({
|
|
110
|
+
id: e.id,
|
|
111
|
+
file: e.env.getTsConfig(),
|
|
112
|
+
}));
|
|
113
|
+
const envsPerFile: { envIds: string[]; fileContent: Record<string, any> }[] = [];
|
|
114
|
+
const isEnvProcessed = (envId: string) =>
|
|
115
|
+
envsPerFile
|
|
116
|
+
.map((e) => e.envIds)
|
|
117
|
+
.flat()
|
|
118
|
+
.find((e) => e === envId);
|
|
119
|
+
envsWithFiles.forEach(({ id, file }) => {
|
|
120
|
+
if (isEnvProcessed(id)) return;
|
|
121
|
+
const foundSameFile = envsWithFiles.filter((e) => isEqual(file, e.file));
|
|
122
|
+
envsPerFile.push({ envIds: foundSameFile.map((f) => f.id), fileContent: file });
|
|
123
|
+
});
|
|
124
|
+
const pathsPerEnvIds = envsPerFile.map((e) => ({
|
|
125
|
+
ids: e.envIds,
|
|
126
|
+
paths: compact(e.envIds.map((envId) => pathsPerEnvs.find((p) => p.id === envId)?.paths).flat()),
|
|
127
|
+
}));
|
|
128
|
+
// const pathsPerEnvIds = pathsPerEnvs.map((p) => ({ ids: p.ids, paths: p.paths }));
|
|
129
|
+
const envsPerDedupedPaths = dedupePaths(pathsPerEnvIds);
|
|
130
|
+
const dedupedPathsPerEnvs: PathsPerEnv[] = envsPerDedupedPaths.map((envWithDedupePaths) => {
|
|
131
|
+
const found = pathsPerEnvs.find((p) => envWithDedupePaths.ids.includes(p.id));
|
|
132
|
+
if (!found) throw new Error(`dedupedPathsPerEnvs, unable to find ${envWithDedupePaths.ids}`);
|
|
133
|
+
return {
|
|
134
|
+
env: found.env,
|
|
135
|
+
ids: envWithDedupePaths.ids,
|
|
136
|
+
paths: envWithDedupePaths.paths,
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return dedupedPathsPerEnvs;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
type PathsPerEnvIds = { ids: string[]; paths: string[] };
|
|
145
|
+
|
|
146
|
+
async function filterDirsWithTsconfigFile(dirs: string[]): Promise<string[]> {
|
|
147
|
+
const dirsWithTsconfig = await Promise.all(
|
|
148
|
+
dirs.map(async (dir) => {
|
|
149
|
+
const hasTsconfig = await fs.pathExists(path.join(dir, 'tsconfig.json'));
|
|
150
|
+
return hasTsconfig ? dir : undefined;
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
return compact(dirsWithTsconfig);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function getAllPossibleDirsFromPaths(paths: PathLinuxRelative[]): PathLinuxRelative[] {
|
|
157
|
+
const dirs = paths.map((p) => getAllParentsDirOfPath(p)).flat();
|
|
158
|
+
dirs.push('.'); // add the root dir
|
|
159
|
+
return uniq(dirs);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function getAllParentsDirOfPath(p: PathLinuxRelative): PathLinuxRelative[] {
|
|
163
|
+
const all: string[] = [];
|
|
164
|
+
let current = p;
|
|
165
|
+
while (current !== '.') {
|
|
166
|
+
all.push(current);
|
|
167
|
+
current = path.dirname(current);
|
|
168
|
+
}
|
|
169
|
+
return all;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* easier to understand by an example:
|
|
174
|
+
* input:
|
|
175
|
+
* [
|
|
176
|
+
* { id: react, paths: [ui/button, ui/form] },
|
|
177
|
+
* { id: aspect, paths: [p/a1, p/a2] },
|
|
178
|
+
* { id: node, paths: [p/n1] },
|
|
179
|
+
* ]
|
|
180
|
+
*
|
|
181
|
+
* output:
|
|
182
|
+
* [
|
|
183
|
+
* { id: react, paths: [ui] },
|
|
184
|
+
* { id: aspect, paths: [p] },
|
|
185
|
+
* { id: node, paths: [p/n1] },
|
|
186
|
+
* ]
|
|
187
|
+
*
|
|
188
|
+
* the goal is to minimize the amount of files to write per env if possible.
|
|
189
|
+
* when multiple components of the same env share a root-dir, then, it's enough to write a file in that shared dir.
|
|
190
|
+
* if in a shared-dir, some components using env1 and some env2, it finds the env that has the max number of
|
|
191
|
+
* components, this env will be optimized. other components, will have the files written inside their dirs.
|
|
192
|
+
*/
|
|
193
|
+
export function dedupePaths(pathsPerEnvId: PathsPerEnvIds[]): PathsPerEnvIds[] {
|
|
194
|
+
const rootDir = '.';
|
|
195
|
+
const individualPathPerConcatenatedEnvIds: { [path: string]: string } = pathsPerEnvId.reduce((acc, current) => {
|
|
196
|
+
current.paths.forEach((p) => {
|
|
197
|
+
acc[p] = current.ids.join(',');
|
|
198
|
+
});
|
|
199
|
+
return acc;
|
|
200
|
+
}, {});
|
|
201
|
+
const allPaths = Object.keys(individualPathPerConcatenatedEnvIds);
|
|
202
|
+
const allPossibleDirs = getAllPossibleDirsFromPaths(allPaths);
|
|
203
|
+
|
|
204
|
+
const allPathsPerEnvId: { [path: string]: string | null } = {}; // null when parent-dir has same amount of comps per env.
|
|
205
|
+
|
|
206
|
+
const calculateBestEnvForDir = (dir: string) => {
|
|
207
|
+
if (individualPathPerConcatenatedEnvIds[dir]) {
|
|
208
|
+
// it's the component dir, so it's the best env
|
|
209
|
+
allPathsPerEnvId[dir] = individualPathPerConcatenatedEnvIds[dir];
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const allPathsShareSameDir = dir === rootDir ? allPaths : allPaths.filter((p) => p.startsWith(`${dir}/`));
|
|
213
|
+
const countPerEnv: { [env: string]: number } = {};
|
|
214
|
+
allPathsShareSameDir.forEach((p) => {
|
|
215
|
+
const envIdStr = individualPathPerConcatenatedEnvIds[p];
|
|
216
|
+
if (countPerEnv[envIdStr]) countPerEnv[envIdStr] += 1;
|
|
217
|
+
else countPerEnv[envIdStr] = 1;
|
|
218
|
+
});
|
|
219
|
+
const max = Math.max(...Object.values(countPerEnv));
|
|
220
|
+
const envWithMax = Object.keys(countPerEnv).filter((env) => countPerEnv[env] === max);
|
|
221
|
+
if (!envWithMax.length) throw new Error(`must be at least one env related to path "${dir}"`);
|
|
222
|
+
if (envWithMax.length > 1) allPathsPerEnvId[dir] = null;
|
|
223
|
+
else allPathsPerEnvId[dir] = envWithMax[0];
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
allPossibleDirs.forEach((dirPath) => {
|
|
227
|
+
calculateBestEnvForDir(dirPath);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// this is the actual deduping. if found a shorter path with the same env, then no need for this path.
|
|
231
|
+
// in other words, return only the paths that their parent is null or has a different env.
|
|
232
|
+
const dedupedPathsPerEnvIds = Object.keys(allPathsPerEnvId).reduce((acc, current) => {
|
|
233
|
+
if (allPathsPerEnvId[current] && allPathsPerEnvId[path.dirname(current)] !== allPathsPerEnvId[current]) {
|
|
234
|
+
acc[current] = allPathsPerEnvId[current];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return acc;
|
|
238
|
+
}, {});
|
|
239
|
+
// rootDir parent is always rootDir, so leave it as is.
|
|
240
|
+
if (allPathsPerEnvId[rootDir]) dedupedPathsPerEnvIds[rootDir] = allPathsPerEnvId[rootDir];
|
|
241
|
+
|
|
242
|
+
const envIdsPerDedupedPaths = invertBy(dedupedPathsPerEnvIds);
|
|
243
|
+
|
|
244
|
+
const dedupedPaths = Object.keys(envIdsPerDedupedPaths).map((envIdStr) => ({
|
|
245
|
+
ids: envIdStr.split(','),
|
|
246
|
+
paths: envIdsPerDedupedPaths[envIdStr],
|
|
247
|
+
}));
|
|
248
|
+
return dedupedPaths;
|
|
249
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,38 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
3
|
"lib": [
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"DOM.Iterable",
|
|
8
|
-
"ScriptHost"
|
|
4
|
+
"esnext",
|
|
5
|
+
"dom",
|
|
6
|
+
"dom.Iterable"
|
|
9
7
|
],
|
|
10
|
-
"target": "
|
|
11
|
-
"module": "
|
|
12
|
-
"jsx": "react",
|
|
13
|
-
"allowJs": true,
|
|
14
|
-
"composite": true,
|
|
8
|
+
"target": "es2020",
|
|
9
|
+
"module": "es2020",
|
|
10
|
+
"jsx": "react-jsx",
|
|
15
11
|
"declaration": true,
|
|
16
12
|
"sourceMap": true,
|
|
17
|
-
"skipLibCheck": true,
|
|
18
13
|
"experimentalDecorators": true,
|
|
19
|
-
"
|
|
14
|
+
"skipLibCheck": true,
|
|
20
15
|
"moduleResolution": "node",
|
|
21
16
|
"esModuleInterop": true,
|
|
22
|
-
"rootDir": ".",
|
|
23
17
|
"resolveJsonModule": true,
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"strictPropertyInitialization": false,
|
|
28
|
-
"strict": true,
|
|
29
|
-
"noImplicitAny": false,
|
|
30
|
-
"preserveConstEnums": true
|
|
18
|
+
"allowJs": true,
|
|
19
|
+
"outDir": "dist",
|
|
20
|
+
"emitDeclarationOnly": true
|
|
31
21
|
},
|
|
32
22
|
"exclude": [
|
|
23
|
+
"artifacts",
|
|
24
|
+
"public",
|
|
33
25
|
"dist",
|
|
26
|
+
"node_modules",
|
|
27
|
+
"package.json",
|
|
34
28
|
"esm.mjs",
|
|
35
|
-
"
|
|
29
|
+
"**/*.cjs",
|
|
30
|
+
"./dist"
|
|
36
31
|
],
|
|
37
32
|
"include": [
|
|
38
33
|
"**/*",
|
package/types/asset.d.ts
CHANGED
|
@@ -5,12 +5,12 @@ declare module '*.png' {
|
|
|
5
5
|
declare module '*.svg' {
|
|
6
6
|
import type { FunctionComponent, SVGProps } from 'react';
|
|
7
7
|
|
|
8
|
-
export const ReactComponent: FunctionComponent<
|
|
8
|
+
export const ReactComponent: FunctionComponent<
|
|
9
|
+
SVGProps<SVGSVGElement> & { title?: string }
|
|
10
|
+
>;
|
|
9
11
|
const src: string;
|
|
10
12
|
export default src;
|
|
11
13
|
}
|
|
12
|
-
|
|
13
|
-
// @TODO Gilad
|
|
14
14
|
declare module '*.jpg' {
|
|
15
15
|
const value: any;
|
|
16
16
|
export = value;
|
|
@@ -27,3 +27,15 @@ declare module '*.bmp' {
|
|
|
27
27
|
const value: any;
|
|
28
28
|
export = value;
|
|
29
29
|
}
|
|
30
|
+
declare module '*.otf' {
|
|
31
|
+
const value: any;
|
|
32
|
+
export = value;
|
|
33
|
+
}
|
|
34
|
+
declare module '*.woff' {
|
|
35
|
+
const value: any;
|
|
36
|
+
export = value;
|
|
37
|
+
}
|
|
38
|
+
declare module '*.woff2' {
|
|
39
|
+
const value: any;
|
|
40
|
+
export = value;
|
|
41
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Logger } from '@teambit/logger';
|
|
2
|
+
import ts from 'typescript';
|
|
3
|
+
import { expect } from 'chai';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { TypescriptAspect } from './typescript.aspect';
|
|
6
|
+
|
|
7
|
+
import { TypescriptCompiler } from './typescript.compiler';
|
|
8
|
+
import type { TsCompilerOptionsWithoutTsConfig } from './compiler-options';
|
|
9
|
+
|
|
10
|
+
const defaultOpts = {
|
|
11
|
+
tsconfig: {},
|
|
12
|
+
types: [],
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
describe('TypescriptCompiler', () => {
|
|
16
|
+
describe('getDistPathBySrcPath', () => {
|
|
17
|
+
it('should replace the extension with .js and prepend the dist dir', () => {
|
|
18
|
+
const tsCompiler = getTsCompiler();
|
|
19
|
+
expect(tsCompiler.getDistPathBySrcPath('index.ts')).to.equal(path.join('dist', 'index.js'));
|
|
20
|
+
expect(tsCompiler.getDistPathBySrcPath('index.tsx')).to.equal(path.join('dist', 'index.js'));
|
|
21
|
+
});
|
|
22
|
+
it('should not replace the extension if the file is not supported', () => {
|
|
23
|
+
const tsCompiler = getTsCompiler();
|
|
24
|
+
expect(tsCompiler.getDistPathBySrcPath('style.css')).to.equal(path.join('dist', 'style.css'));
|
|
25
|
+
expect(tsCompiler.getDistPathBySrcPath('index.d.ts')).to.equal(path.join('dist', 'index.d.ts'));
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe('isFileSupported', () => {
|
|
29
|
+
it('should support .ts files', () => {
|
|
30
|
+
const tsCompiler = getTsCompiler();
|
|
31
|
+
expect(tsCompiler.isFileSupported('index.ts')).to.be.true;
|
|
32
|
+
});
|
|
33
|
+
it('should support .tsx files', () => {
|
|
34
|
+
const tsCompiler = getTsCompiler();
|
|
35
|
+
expect(tsCompiler.isFileSupported('index.tsx')).to.be.true;
|
|
36
|
+
});
|
|
37
|
+
it('should not support .jsx files by default', () => {
|
|
38
|
+
const tsCompiler = getTsCompiler();
|
|
39
|
+
expect(tsCompiler.isFileSupported('index.jsx')).to.be.false;
|
|
40
|
+
});
|
|
41
|
+
it('should not support .js files by default', () => {
|
|
42
|
+
const tsCompiler = getTsCompiler();
|
|
43
|
+
expect(tsCompiler.isFileSupported('index.js')).to.be.false;
|
|
44
|
+
});
|
|
45
|
+
it('should support .jsx files when passing compileJsx', () => {
|
|
46
|
+
const tsCompiler = getTsCompiler({ compileJsx: true, ...defaultOpts });
|
|
47
|
+
expect(tsCompiler.isFileSupported('index.jsx')).to.be.true;
|
|
48
|
+
});
|
|
49
|
+
it('should support .js files when passing compileJs', () => {
|
|
50
|
+
const tsCompiler = getTsCompiler({ compileJs: true, ...defaultOpts });
|
|
51
|
+
expect(tsCompiler.isFileSupported('index.js')).to.be.true;
|
|
52
|
+
});
|
|
53
|
+
it('should not support .d.ts files', () => {
|
|
54
|
+
const tsCompiler = getTsCompiler();
|
|
55
|
+
expect(tsCompiler.isFileSupported('index.d.ts')).to.be.false;
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
function getTsCompiler(opts: TsCompilerOptionsWithoutTsConfig = defaultOpts) {
|
|
61
|
+
const finalOpts = Object.assign({}, defaultOpts, opts);
|
|
62
|
+
return new TypescriptCompiler(TypescriptAspect.id, new Logger('test'), finalOpts, ts);
|
|
63
|
+
}
|