Skip to content

Commit 4b175ad

Browse files
feat: add support for TS by pre-compiling it before parsing with babel.
1 parent 81e2d28 commit 4b175ad

8 files changed

+97
-55
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# Changelog
22

3+
## 1.5.0 - 2024-04-26
4+
5+
### Bug fixes
6+
7+
- Fix error when babel config is not found by returning an empty array.
8+
- Add guard to `auditArgumentChecks` since `node` could be empty.
9+
10+
### Changes
11+
12+
- Add support to `TS` and `TSX` files by `pre-compiling` it with the `typescript` compiler.
13+
314
## 1.4.2 - 2024-03-22
415

516
### Bug fixes

lib/rules/audit-argument-checks.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ module.exports = {
4747
}
4848

4949
function auditArgumentChecks(node) {
50-
if (!isFunction(node.type)) {
50+
if (!node || !isFunction(node.type)) {
5151
return;
5252
}
5353

lib/rules/no-sync-mongo-methods-on-server.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ module.exports = {
7979
archList: ['server'],
8080
isTest,
8181
onFile: ({ path }) => {
82-
debug(`Processing file ${path}`);
82+
debug(`Processing file no-sync-mongo-methods-on-server ${path}`);
8383
},
8484
});
8585
},

lib/rules/no-sync-user-methods-on-server.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
const { Walker, getInitFolder, shouldSkip } = require('../util/walker');
9+
const { debug } = require("../util/utilities");
910

1011
function isCallingUserMethod(node) {
1112
const isCallExpression = node &&
@@ -49,7 +50,9 @@ module.exports = {
4950
new Walker(getInitFolder(context)).walkApp({
5051
archList: ['server'],
5152
isTest,
52-
onFile: () => {},
53+
onFile: ({ path }) => {
54+
debug(`Processing file no-sync-user-methods-on-server ${path}`);
55+
},
5356
});
5457
},
5558
CallExpression: function(node) {

lib/util/parse.js

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function findBabelConfig(startDir, appDir) {
2323

2424
const parentDir = path.resolve(startDir, '..');
2525
if (!parentDir.includes(appDir)) {
26-
return false;
26+
return [];
2727
}
2828

2929
return findBabelConfig(parentDir, appDir);
@@ -47,10 +47,48 @@ function findModuleResolveConfig(filePath, appDir) {
4747
}
4848
}
4949

50+
function precompileTypeScript(filePath, fileContent) {
51+
if (filePath && !filePath.endsWith(".ts") && !filePath.endsWith(".tsx")) {
52+
throw new Error("Trying to precompile a non TS file");
53+
}
54+
55+
const ts = require("typescript");
56+
try {
57+
return ts.transpileModule(fileContent, {
58+
filePath,
59+
compilerOptions: {
60+
target: ts.ScriptTarget.ESNext,
61+
// Leave module syntax intact so that Babel/Reify can handle it.
62+
module: ts.ModuleKind.ESNext,
63+
// This used to be false by default, but appears to have become
64+
// true by default around the release of [email protected]. It's
65+
// important to disable this option because enabling it allows
66+
// TypeScript to use helpers like __importDefault, which are much
67+
// better handled by Babel/Reify later in the pipeline.
68+
esModuleInterop: false,
69+
sourceMap: false,
70+
inlineSources: false,
71+
experimentalDecorators: true,
72+
emitDecoratorMetadata: true,
73+
jsx: ts.JsxEmit.React,
74+
}
75+
});
76+
} catch (e) {
77+
e.message = `While compiling ${filePath}: ${e.message}`;
78+
throw e;
79+
}
80+
}
81+
5082
module.exports = function readAndParse(filePath) {
51-
const content = fs.readFileSync(filePath, 'utf-8');
83+
if (!filePath) {
84+
throw Error("Missing file path");
85+
}
5286

53-
const ast = parse(content, {
87+
const fileContent = fs.readFileSync(filePath, 'utf-8');
88+
const isTsFile = filePath.endsWith(".ts") || filePath.endsWith("tsx");
89+
const codeContent = isTsFile ? precompileTypeScript(filePath, fileContent).outputText : fileContent;
90+
91+
return parse(codeContent, {
5492
parser: {
5593
parse: (source) =>
5694
meteorBabelParser(source, {
@@ -59,8 +97,6 @@ module.exports = function readAndParse(filePath) {
5997
}),
6098
},
6199
});
62-
63-
return ast;
64100
};
65101

66102
module.exports.findImports = function findImports(filePath, ast, appDir) {

lib/util/walker.js

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@ function shouldWalk(folderPath, archList) {
5353

5454
function findExt(filePath) {
5555
const ext = parseableExt.find((possibleExt) => {
56-
const exists = fs.existsSync(filePath + possibleExt);
57-
return exists;
56+
return fs.existsSync(filePath + possibleExt);
5857
});
5958

6059
if (ext) {
@@ -103,15 +102,11 @@ function getAbsFilePath(filePath) {
103102
// some files have no ext or are only the ext (.gitignore, .editorconfig, etc.)
104103
const existingExt =
105104
path.extname(filePath) || path.basename(filePath).startsWith('.');
106-
if (!existingExt) {
105+
106+
if (!existingExt || !parseableExt.includes(existingExt)) {
107107
// TODO: should maybe only do this if a file doesn't exists with the given path
108108
// since we might be importing a file with no extension.
109-
const pathWithExt = findExt(filePath);
110-
if (!pathWithExt) {
111-
return pathWithExt;
112-
}
113-
114-
return pathWithExt;
109+
return findExt(filePath);
115110
}
116111

117112
// TODO: if the file doesn't exist, we must try other extensions
@@ -255,32 +250,27 @@ class Walker {
255250
debug('Cache is not going to be used');
256251
}
257252
this.cachedParsedFile = useCache
258-
? JSON.parse(fs.readFileSync(this.filePath()))
253+
? JSON.parse(fs.readFileSync(this.filePath(), 'utf-8'))
259254
: {};
260255
}
261-
walkApp({ archList, onFile, isTest }) {
256+
walkApp({ archList, onFile, isTest }) {
262257
if (Object.keys(this.cachedParsedFile).length > 0) {
263258
debug('Not walking the app as the cachedParsedFile is already present');
264259
return;
265260
}
266261

267-
let initialServerFile;
268262
const packageJsonLocation = path.join(this.appPath, 'package.json');
269263
if (fs.existsSync(packageJsonLocation)) {
270-
const content = JSON.parse(fs.readFileSync(packageJsonLocation));
271-
const { meteor } = content;
264+
const { meteor } = JSON.parse(fs.readFileSync(packageJsonLocation, 'utf-8'));
272265
if (meteor && meteor.mainModule && meteor.mainModule.server) {
273-
initialServerFile = path.join(this.appPath, meteor.mainModule.server);
274266
debug('Starting from meteor.mainModule.server from package.json', meteor.mainModule.server);
267+
handleFile(
268+
path.join(this.appPath, meteor.mainModule.server),
269+
this.appPath,
270+
onFile,
271+
this.cachedParsedFile
272+
);
275273
}
276-
}
277-
if (initialServerFile) {
278-
handleFile(
279-
initialServerFile,
280-
this.appPath,
281-
onFile,
282-
this.cachedParsedFile
283-
);
284274
} else {
285275
handleFolder(
286276
this.appPath,
@@ -290,8 +280,10 @@ class Walker {
290280
this.cachedParsedFile
291281
);
292282
}
283+
293284
fs.writeFileSync(this.filePath(), JSON.stringify(this.cachedParsedFile));
294285
}
286+
295287
get cachedParsedFile() {
296288
return this.cachedParsedFile;
297289
}

package-lock.json

Lines changed: 23 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"readline-sync": "^1.4.10",
3737
"recast": "^0.23.4",
3838
"reify": "^0.20.12",
39+
"typescript": "^5.4.5",
3940
"validate-commit-msg": "^2.14.0"
4041
},
4142
"peerDependencies": {

0 commit comments

Comments
 (0)