@tanstack/router-cli 0.0.1-beta.69 → 1.0.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/LICENSE +1 -1
- package/bin/tsr.js +1 -1
- package/build/cjs/config.js +14 -10
- package/build/cjs/config.js.map +1 -1
- package/build/cjs/generate.js +1 -3
- package/build/cjs/generate.js.map +1 -1
- package/build/cjs/generator.js +203 -237
- package/build/cjs/generator.js.map +1 -1
- package/build/cjs/index.js +4 -7
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/watch.js +17 -24
- package/build/cjs/watch.js.map +1 -1
- package/build/types/config.d.ts +20 -0
- package/build/types/generate.d.ts +2 -0
- package/build/types/generator.d.ts +20 -0
- package/build/types/index.d.ts +1 -13
- package/build/types/watch.d.ts +1 -0
- package/package.json +8 -2
- package/src/config.ts +13 -8
- package/src/generator.ts +290 -394
- package/src/watch.ts +19 -16
- package/build/cjs/transformCode.js +0 -584
- package/build/cjs/transformCode.js.map +0 -1
- package/build/esm/index.js +0 -903
- package/build/esm/index.js.map +0 -1
- package/src/transformCode.ts +0 -860
package/build/esm/index.js
DELETED
|
@@ -1,903 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* router-cli
|
|
3
|
-
*
|
|
4
|
-
* Copyright (c) TanStack
|
|
5
|
-
*
|
|
6
|
-
* This source code is licensed under the MIT license found in the
|
|
7
|
-
* LICENSE.md file in the root directory of this source tree.
|
|
8
|
-
*
|
|
9
|
-
* @license MIT
|
|
10
|
-
*/
|
|
11
|
-
import * as yargs from 'yargs';
|
|
12
|
-
import path from 'path';
|
|
13
|
-
import fs from 'fs-extra';
|
|
14
|
-
import klaw from 'klaw';
|
|
15
|
-
import through2 from 'through2';
|
|
16
|
-
import crypto from 'crypto';
|
|
17
|
-
import * as babel from '@babel/core';
|
|
18
|
-
import * as t from '@babel/types';
|
|
19
|
-
import syntaxTS from '@babel/plugin-syntax-typescript';
|
|
20
|
-
import chokidar from 'chokidar';
|
|
21
|
-
|
|
22
|
-
const configFilePathJson = path.resolve(process.cwd(), 'tsr.config.json');
|
|
23
|
-
async function getConfig() {
|
|
24
|
-
return fs.readJson(configFilePathJson);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const isolatedProperties = ['component', 'errorComponent', 'pendingComponent'];
|
|
28
|
-
const getBasePlugins = () => [[syntaxTS, {
|
|
29
|
-
isTSX: true
|
|
30
|
-
}]];
|
|
31
|
-
async function ensureBoilerplate(node, code) {
|
|
32
|
-
const relativeImportPath = path.relative(node.fullDir, node.genPathNoExt);
|
|
33
|
-
const originalFile = await babel.transformAsync(code, {
|
|
34
|
-
configFile: false,
|
|
35
|
-
babelrc: false,
|
|
36
|
-
plugins: [...getBasePlugins()]
|
|
37
|
-
});
|
|
38
|
-
const file = await babel.transformAsync(code, {
|
|
39
|
-
configFile: false,
|
|
40
|
-
babelrc: false,
|
|
41
|
-
plugins: [...getBasePlugins(), {
|
|
42
|
-
visitor: {
|
|
43
|
-
Program: {
|
|
44
|
-
enter(programPath) {
|
|
45
|
-
// Remove all properties except for our isolated one
|
|
46
|
-
if (node.isRoot) {
|
|
47
|
-
let foundImport = false;
|
|
48
|
-
programPath.traverse({
|
|
49
|
-
ImportSpecifier(importPath) {
|
|
50
|
-
if (t.isIdentifier(importPath.node.imported) && importPath.node.imported.name === 'Route') {
|
|
51
|
-
foundImport = true;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
if (!foundImport) {
|
|
56
|
-
programPath.node.body.unshift(babel.template.statement(`import { Route } from '@tanstack/react-router'`)());
|
|
57
|
-
}
|
|
58
|
-
} else {
|
|
59
|
-
let foundImport = false;
|
|
60
|
-
programPath.traverse({
|
|
61
|
-
ImportSpecifier(importPath) {
|
|
62
|
-
if (t.isIdentifier(importPath.node.imported) && importPath.node.imported.name === 'route') {
|
|
63
|
-
foundImport = true;
|
|
64
|
-
if (t.isImportDeclaration(importPath.parentPath.node)) {
|
|
65
|
-
if (importPath.parentPath.node.source.value !== relativeImportPath) {
|
|
66
|
-
importPath.parentPath.node.source.value = relativeImportPath;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
if (!foundImport) {
|
|
73
|
-
programPath.node.body.unshift(babel.template.statement(`import { Route } from '${relativeImportPath.replace(/\\/gi, '/')}'`)());
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}]
|
|
80
|
-
});
|
|
81
|
-
const separator = 'new Route(';
|
|
82
|
-
if (!originalFile?.code) {
|
|
83
|
-
return `${file?.code}\n\n${separator}{\n\n})`;
|
|
84
|
-
}
|
|
85
|
-
const originalHead = originalFile?.code?.substring(0, originalFile?.code?.indexOf(separator));
|
|
86
|
-
const generatedHead = file?.code?.substring(0, file?.code?.indexOf(separator));
|
|
87
|
-
if (originalHead !== generatedHead) {
|
|
88
|
-
return `${generatedHead}\n\n${originalFile?.code?.substring(originalFile?.code?.indexOf(separator))}`;
|
|
89
|
-
}
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
async function isolateOptionToExport(node, code, opts) {
|
|
93
|
-
return (await babel.transformAsync(code, {
|
|
94
|
-
configFile: false,
|
|
95
|
-
babelrc: false,
|
|
96
|
-
plugins: [...getBasePlugins(), plugin()],
|
|
97
|
-
ast: true
|
|
98
|
-
}))?.code;
|
|
99
|
-
function plugin() {
|
|
100
|
-
return {
|
|
101
|
-
visitor: {
|
|
102
|
-
Program: {
|
|
103
|
-
enter(programPath, state) {
|
|
104
|
-
// If we're the root, handle things a bit differently
|
|
105
|
-
if (node.isRoot) {
|
|
106
|
-
programPath.traverse({
|
|
107
|
-
Identifier(path) {
|
|
108
|
-
if (path.node.name === 'Route' && t.isCallExpression(path.parentPath.node)) {
|
|
109
|
-
const options = getRouteOptions(path);
|
|
110
|
-
if (options) {
|
|
111
|
-
const property = options.properties.find(property => {
|
|
112
|
-
return t.isObjectProperty(property) && t.isIdentifier(property.key) && property.key.name === opts.isolate;
|
|
113
|
-
});
|
|
114
|
-
if (t.isObjectProperty(property)) {
|
|
115
|
-
const program = path.findParent(d => d.isProgram());
|
|
116
|
-
if (program?.isProgram()) {
|
|
117
|
-
program.node.body.push(babel.template.statement(`export const ${opts.isolate} = $VAR`)({
|
|
118
|
-
$VAR: property.value
|
|
119
|
-
}));
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
path.findParent(d => d.isExpressionStatement())?.remove();
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// We're not in the root, handle things normally
|
|
130
|
-
if (!node.isRoot) {
|
|
131
|
-
// Remove all properties except for our isolated one
|
|
132
|
-
programPath.traverse({
|
|
133
|
-
Identifier(path) {
|
|
134
|
-
if (path.node.name === 'generate') {
|
|
135
|
-
const options = getRouteConfigGenerateOptions(path);
|
|
136
|
-
if (options) {
|
|
137
|
-
const property = options.properties.find(property => {
|
|
138
|
-
return t.isObjectProperty(property) && t.isIdentifier(property.key) && property.key.name === opts.isolate;
|
|
139
|
-
});
|
|
140
|
-
if (t.isObjectProperty(property) && t.isIdentifier(property.key)) {
|
|
141
|
-
if (property.key.name === opts.isolate) {
|
|
142
|
-
const program = path.findParent(d => d.isProgram());
|
|
143
|
-
if (program?.isProgram()) {
|
|
144
|
-
program.node.body.push(babel.template.statement(`export const ${opts.isolate} = $VAR`)({
|
|
145
|
-
$VAR: property.value
|
|
146
|
-
}));
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
path.findParent(d => d.isExpressionStatement())?.remove();
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
cleanUnusedCode(programPath, state, [opts.isolate]);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
async function detectExports(code) {
|
|
164
|
-
let exported = [];
|
|
165
|
-
|
|
166
|
-
// try {
|
|
167
|
-
await babel.transformAsync(code, {
|
|
168
|
-
configFile: false,
|
|
169
|
-
babelrc: false,
|
|
170
|
-
plugins: [...getBasePlugins(), {
|
|
171
|
-
visitor: {
|
|
172
|
-
ExportNamedDeclaration(path) {
|
|
173
|
-
if (t.isVariableDeclaration(path.node.declaration)) {
|
|
174
|
-
if (t.isVariableDeclarator(path.node.declaration.declarations?.[0])) {
|
|
175
|
-
if (t.isIdentifier(path.node.declaration.declarations[0].id)) {
|
|
176
|
-
exported.push(path.node.declaration.declarations[0].id.name);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}],
|
|
183
|
-
ast: true
|
|
184
|
-
});
|
|
185
|
-
return exported;
|
|
186
|
-
}
|
|
187
|
-
async function generateRouteConfig(node, routeCode, imports, clientOnly) {
|
|
188
|
-
const relativeParentRoutePath = clientOnly ? node.parent ? removeExt(path.relative(node.genDir, path.resolve(node.parent?.genDir, node.parent?.clientFilename))) : `./${rootRouteClientName}` : node.parent ? removeExt(path.relative(node.genDir, path.resolve(node.parent?.genDir, node.parent?.filename))) : `./${rootRouteName}`;
|
|
189
|
-
const pathName = node.isRoot ? undefined : node.fileNameNoExt.startsWith('__') ? undefined : node.fileNameNoExt === 'index' ? '/' : node.fileNameNoExt;
|
|
190
|
-
const routeId = node.isRoot ? undefined : node.fileNameNoExt;
|
|
191
|
-
function plugin() {
|
|
192
|
-
return {
|
|
193
|
-
visitor: {
|
|
194
|
-
Program: {
|
|
195
|
-
enter(programPath, state) {
|
|
196
|
-
// Remove all of the isolated import properties from the config
|
|
197
|
-
programPath.traverse({
|
|
198
|
-
ImportSpecifier(path) {
|
|
199
|
-
if (t.isIdentifier(path.node.imported)) {
|
|
200
|
-
if (!node.isRoot) {
|
|
201
|
-
if (path.node.imported.name === 'route') {
|
|
202
|
-
path.parentPath.remove();
|
|
203
|
-
const program = path.findParent(d => d.isProgram());
|
|
204
|
-
if (program?.isProgram()) {
|
|
205
|
-
program.node.body.unshift(babel.template.statement(`import { route as parentRoute } from '$IMPORT'`)({
|
|
206
|
-
$IMPORT: relativeParentRoutePath
|
|
207
|
-
}));
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
},
|
|
213
|
-
Identifier(iPath) {
|
|
214
|
-
let options;
|
|
215
|
-
if (node.isRoot) {
|
|
216
|
-
if (iPath.node.name === 'Route') {
|
|
217
|
-
if (t.isCallExpression(iPath.parentPath.node)) {
|
|
218
|
-
if (t.isExpressionStatement(iPath.parentPath.parentPath?.node)) {
|
|
219
|
-
iPath.parentPath.parentPath?.replaceWith(t.variableDeclaration('const', [t.variableDeclarator(t.identifier('route'), iPath.parentPath.node)]));
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
} else {
|
|
224
|
-
if (iPath.node.name === 'generate') {
|
|
225
|
-
if (t.isMemberExpression(iPath.parentPath.node)) {
|
|
226
|
-
if (t.isIdentifier(iPath.parentPath.node.object)) {
|
|
227
|
-
iPath.node.name = 'Route';
|
|
228
|
-
iPath.parentPath.node.object.name = 'parentRoute';
|
|
229
|
-
options = getRouteConfigGenerateOptions(iPath);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
if (options) {
|
|
235
|
-
options.properties = [...(pathName ? [t.objectProperty(t.identifier('path'), t.stringLiteral(pathName))] : routeId ? [t.objectProperty(t.identifier('id'), t.stringLiteral(routeId))] : []), ...options.properties.map(property => {
|
|
236
|
-
if (t.isObjectProperty(property) && t.isIdentifier(property.key) && isolatedProperties.includes(property.key.name)) {
|
|
237
|
-
const key = property.key.name;
|
|
238
|
-
if (key === 'loader') {
|
|
239
|
-
if (clientOnly) {
|
|
240
|
-
return t.objectProperty(t.identifier('loader'), t.tSAsExpression(t.booleanLiteral(true), t.tsAnyKeyword()));
|
|
241
|
-
}
|
|
242
|
-
return t.objectProperty(t.identifier(key), babel.template.expression(`(...args) => import('./${path.relative(node.genDir, node.genPathNoExt)}-${key}').then(d => d.${key}.apply(d.${key}, (args as any)))`, {
|
|
243
|
-
plugins: ['typescript']
|
|
244
|
-
})({}));
|
|
245
|
-
}
|
|
246
|
-
if (key === 'action') {
|
|
247
|
-
if (clientOnly) {
|
|
248
|
-
return t.objectProperty(t.identifier('action'), t.tSAsExpression(t.booleanLiteral(true), t.tSAnyKeyword()));
|
|
249
|
-
}
|
|
250
|
-
return t.objectProperty(t.identifier(key), babel.template.expression(`(...payload: Parameters<typeof import('./${path.relative(node.genDir, node.genPathNoExt)}-${key}').action>) => import('./${path.relative(node.genDir, node.genPathNoExt)}-${key}').then(d => d.${key}.apply(d.${key}, (payload as any)))`, {
|
|
251
|
-
plugins: ['typescript']
|
|
252
|
-
})({}));
|
|
253
|
-
}
|
|
254
|
-
if (clientOnly) {
|
|
255
|
-
return t.objectProperty(t.identifier(key), babel.template.expression(`
|
|
256
|
-
lazy(() => import('./${path.relative(node.genDir, node.genPathNoExt)}-${key}').then(d => ({ default: d.${key} }) ))`)());
|
|
257
|
-
}
|
|
258
|
-
return t.objectProperty(t.identifier(key), property.value);
|
|
259
|
-
}
|
|
260
|
-
return property;
|
|
261
|
-
})];
|
|
262
|
-
const program = iPath.findParent(d => d.isProgram());
|
|
263
|
-
if (program?.isProgram() && options) {
|
|
264
|
-
const index = program.node.body.findIndex(d => d.start === iPath.parentPath.parentPath?.node.start);
|
|
265
|
-
if (node.isRoot) {
|
|
266
|
-
program.node.body[index] = babel.template.statement(`const route = new Route(
|
|
267
|
-
$OPTIONS
|
|
268
|
-
)`)({
|
|
269
|
-
$OPTIONS: options
|
|
270
|
-
});
|
|
271
|
-
} else {
|
|
272
|
-
program.node.body[index] = babel.template.statement(`const route = new Route(
|
|
273
|
-
$OPTIONS
|
|
274
|
-
)`)({
|
|
275
|
-
$OPTIONS: options
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
});
|
|
282
|
-
programPath.node.body.unshift(babel.template.statement(`import { lazy } from '@tanstack/react-router'`)());
|
|
283
|
-
|
|
284
|
-
// Add the route exports
|
|
285
|
-
programPath.node.body.push(babel.template.statement(clientOnly ? `export { route, route as ${node.variable}Route }` : `export { route }`)());
|
|
286
|
-
cleanUnusedCode(programPath, state, ['route', `${node.variable}Route`]);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
const code = (await babel.transformAsync(routeCode, {
|
|
293
|
-
configFile: false,
|
|
294
|
-
babelrc: false,
|
|
295
|
-
plugins: [...getBasePlugins(), plugin()],
|
|
296
|
-
ast: true
|
|
297
|
-
}))?.code;
|
|
298
|
-
if (!code) {
|
|
299
|
-
throw new Error('Error while generating a route file!');
|
|
300
|
-
}
|
|
301
|
-
return code;
|
|
302
|
-
}
|
|
303
|
-
function getIdentifier(path) {
|
|
304
|
-
const parentPath = path.parentPath;
|
|
305
|
-
if (parentPath.type === 'VariableDeclarator') {
|
|
306
|
-
const pp = parentPath;
|
|
307
|
-
const name = pp.get('id');
|
|
308
|
-
return name.node.type === 'Identifier' ? name : null;
|
|
309
|
-
}
|
|
310
|
-
if (parentPath.type === 'AssignmentExpression') {
|
|
311
|
-
const pp = parentPath;
|
|
312
|
-
const name = pp.get('left');
|
|
313
|
-
return name.node.type === 'Identifier' ? name : null;
|
|
314
|
-
}
|
|
315
|
-
if (path.node.type === 'ArrowFunctionExpression') {
|
|
316
|
-
return null;
|
|
317
|
-
}
|
|
318
|
-
return path.node.id && path.node.id.type === 'Identifier' ? path.get('id') : null;
|
|
319
|
-
}
|
|
320
|
-
function isIdentifierReferenced(ident) {
|
|
321
|
-
const b = ident.scope.getBinding(ident.node.name);
|
|
322
|
-
if (b && b.referenced) {
|
|
323
|
-
if (b.path.type === 'FunctionDeclaration') {
|
|
324
|
-
return !b.constantViolations.concat(b.referencePaths).every(ref => ref.findParent(p => p === b.path));
|
|
325
|
-
}
|
|
326
|
-
return true;
|
|
327
|
-
}
|
|
328
|
-
return false;
|
|
329
|
-
}
|
|
330
|
-
function markFunction(path, state) {
|
|
331
|
-
const ident = getIdentifier(path);
|
|
332
|
-
if (ident && ident.node && isIdentifierReferenced(ident)) {
|
|
333
|
-
state.refs.add(ident);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
function markImport(path, state) {
|
|
337
|
-
const local = path.get('local');
|
|
338
|
-
if (isIdentifierReferenced(local)) {
|
|
339
|
-
state.refs.add(local);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
function getRouteConfigGenerateOptions(path) {
|
|
343
|
-
const tryOptions = node => {
|
|
344
|
-
if (t.isIdentifier(node)) {
|
|
345
|
-
const initNode = path.scope.getBinding(node.name)?.path.node;
|
|
346
|
-
if (t.isVariableDeclarator(initNode)) {
|
|
347
|
-
return tryOptions(initNode.init);
|
|
348
|
-
}
|
|
349
|
-
} else if (t.isObjectExpression(node)) {
|
|
350
|
-
return node;
|
|
351
|
-
}
|
|
352
|
-
return;
|
|
353
|
-
};
|
|
354
|
-
if (t.isMemberExpression(path.parentPath.node) && t.isCallExpression(path.parentPath.parentPath?.node)) {
|
|
355
|
-
const options = path.parentPath.parentPath?.node.arguments[0];
|
|
356
|
-
return tryOptions(options);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
function getRouteOptions(path) {
|
|
360
|
-
const tryOptions = node => {
|
|
361
|
-
if (t.isIdentifier(node)) {
|
|
362
|
-
const initNode = path.scope.getBinding(node.name)?.path.node;
|
|
363
|
-
if (t.isVariableDeclarator(initNode)) {
|
|
364
|
-
return tryOptions(initNode.init);
|
|
365
|
-
}
|
|
366
|
-
} else if (t.isObjectExpression(node)) {
|
|
367
|
-
return node;
|
|
368
|
-
}
|
|
369
|
-
return;
|
|
370
|
-
};
|
|
371
|
-
if (t.isCallExpression(path.parentPath?.node)) {
|
|
372
|
-
const options = path.parentPath?.node.arguments[0];
|
|
373
|
-
return tryOptions(options);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// All credit for this amazing function goes to the Next.js team
|
|
378
|
-
// (and the Solid.js team for their derivative work).
|
|
379
|
-
// https://github.com/vercel/next.js/blob/canary/packages/next/build/babel/plugins/next-ssg-transform.ts
|
|
380
|
-
// https://github.com/solidjs/solid-start/blob/main/packages/start/server/routeData.js
|
|
381
|
-
|
|
382
|
-
function cleanUnusedCode(programPath, state, keepExports = []) {
|
|
383
|
-
state.refs = new Set();
|
|
384
|
-
state.done = false;
|
|
385
|
-
function markVariable(variablePath, variableState) {
|
|
386
|
-
if (variablePath.node.id.type === 'Identifier') {
|
|
387
|
-
const local = variablePath.get('id');
|
|
388
|
-
if (isIdentifierReferenced(local)) {
|
|
389
|
-
variableState.refs.add(local);
|
|
390
|
-
}
|
|
391
|
-
} else if (variablePath.node.id.type === 'ObjectPattern') {
|
|
392
|
-
const pattern = variablePath.get('id');
|
|
393
|
-
const properties = pattern.get('properties');
|
|
394
|
-
properties.forEach(p => {
|
|
395
|
-
const local = p.get(p.node.type === 'ObjectProperty' ? 'value' : p.node.type === 'RestElement' ? 'argument' : function () {
|
|
396
|
-
throw new Error('invariant');
|
|
397
|
-
}());
|
|
398
|
-
if (isIdentifierReferenced(local)) {
|
|
399
|
-
variableState.refs.add(local);
|
|
400
|
-
}
|
|
401
|
-
});
|
|
402
|
-
} else if (variablePath.node.id.type === 'ArrayPattern') {
|
|
403
|
-
const pattern = variablePath.get('id');
|
|
404
|
-
const elements = pattern.get('elements');
|
|
405
|
-
elements.forEach(e => {
|
|
406
|
-
let local;
|
|
407
|
-
if (e.node && e.node.type === 'Identifier') {
|
|
408
|
-
local = e;
|
|
409
|
-
} else if (e.node && e.node.type === 'RestElement') {
|
|
410
|
-
local = e.get('argument');
|
|
411
|
-
} else {
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
if (isIdentifierReferenced(local)) {
|
|
415
|
-
variableState.refs.add(local);
|
|
416
|
-
}
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Mark all variables and functions if used
|
|
422
|
-
programPath.traverse({
|
|
423
|
-
VariableDeclarator: markVariable,
|
|
424
|
-
FunctionDeclaration: markFunction,
|
|
425
|
-
FunctionExpression: markFunction,
|
|
426
|
-
ArrowFunctionExpression: markFunction,
|
|
427
|
-
ImportSpecifier: markImport,
|
|
428
|
-
ImportDefaultSpecifier: markImport,
|
|
429
|
-
ImportNamespaceSpecifier: markImport,
|
|
430
|
-
ExportDefaultDeclaration: markImport,
|
|
431
|
-
// ExportNamedDeclaration(path, state) {
|
|
432
|
-
// if (t.isVariableDeclaration(path.node.declaration)) {
|
|
433
|
-
// if (t.isVariableDeclarator(path.node.declaration.declarations?.[0])) {
|
|
434
|
-
// if (t.isIdentifier(path.node.declaration.declarations[0].id)) {
|
|
435
|
-
// if (
|
|
436
|
-
// keepExports.includes(
|
|
437
|
-
// path.node.declaration.declarations[0].id.name,
|
|
438
|
-
// )
|
|
439
|
-
// ) {
|
|
440
|
-
// return
|
|
441
|
-
// }
|
|
442
|
-
// }
|
|
443
|
-
// path.replaceWith(path.node.declaration.declarations[0])
|
|
444
|
-
// return
|
|
445
|
-
// }
|
|
446
|
-
// }
|
|
447
|
-
// path.remove()
|
|
448
|
-
// },
|
|
449
|
-
ImportDeclaration: path => {
|
|
450
|
-
if (path.node.source.value.endsWith('.css')) {
|
|
451
|
-
path.remove();
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
}, state);
|
|
455
|
-
|
|
456
|
-
// Sweet all of the remaining references and remove unused ones
|
|
457
|
-
const refs = state.refs;
|
|
458
|
-
let count;
|
|
459
|
-
function sweepFunction(sweepPath) {
|
|
460
|
-
const ident = getIdentifier(sweepPath);
|
|
461
|
-
if (ident && ident.node && refs.has(ident) && !isIdentifierReferenced(ident)) {
|
|
462
|
-
++count;
|
|
463
|
-
if (t.isAssignmentExpression(sweepPath.parentPath) || t.isVariableDeclarator(sweepPath.parentPath)) {
|
|
464
|
-
sweepPath.parentPath.remove();
|
|
465
|
-
} else {
|
|
466
|
-
sweepPath.remove();
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
function sweepImport(sweepPath) {
|
|
471
|
-
const local = sweepPath.get('local');
|
|
472
|
-
if (refs.has(local) && !isIdentifierReferenced(local)) {
|
|
473
|
-
++count;
|
|
474
|
-
sweepPath.remove();
|
|
475
|
-
if (sweepPath.parent.specifiers.length === 0) {
|
|
476
|
-
sweepPath.parentPath.remove();
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
do {
|
|
481
|
-
programPath.scope.crawl();
|
|
482
|
-
count = 0;
|
|
483
|
-
programPath.traverse({
|
|
484
|
-
VariableDeclarator(variablePath) {
|
|
485
|
-
if (variablePath.node.id.type === 'Identifier') {
|
|
486
|
-
const local = variablePath.get('id');
|
|
487
|
-
if (refs.has(local) && !isIdentifierReferenced(local)) {
|
|
488
|
-
++count;
|
|
489
|
-
variablePath.remove();
|
|
490
|
-
}
|
|
491
|
-
} else if (variablePath.node.id.type === 'ObjectPattern') {
|
|
492
|
-
const pattern = variablePath.get('id');
|
|
493
|
-
const beforeCount = count;
|
|
494
|
-
const properties = pattern.get('properties');
|
|
495
|
-
properties.forEach(p => {
|
|
496
|
-
const local = p.get(p.node.type === 'ObjectProperty' ? 'value' : p.node.type === 'RestElement' ? 'argument' : function () {
|
|
497
|
-
throw new Error('invariant');
|
|
498
|
-
}());
|
|
499
|
-
if (refs.has(local) && !isIdentifierReferenced(local)) {
|
|
500
|
-
++count;
|
|
501
|
-
p.remove();
|
|
502
|
-
}
|
|
503
|
-
});
|
|
504
|
-
if (beforeCount !== count && pattern.get('properties').length < 1) {
|
|
505
|
-
variablePath.remove();
|
|
506
|
-
}
|
|
507
|
-
} else if (variablePath.node.id.type === 'ArrayPattern') {
|
|
508
|
-
const pattern = variablePath.get('id');
|
|
509
|
-
const beforeCount = count;
|
|
510
|
-
const elements = pattern.get('elements');
|
|
511
|
-
elements.forEach(e => {
|
|
512
|
-
let local;
|
|
513
|
-
if (e.node && e.node.type === 'Identifier') {
|
|
514
|
-
local = e;
|
|
515
|
-
} else if (e.node && e.node.type === 'RestElement') {
|
|
516
|
-
local = e.get('argument');
|
|
517
|
-
} else {
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
if (refs.has(local) && !isIdentifierReferenced(local)) {
|
|
521
|
-
++count;
|
|
522
|
-
e.remove();
|
|
523
|
-
}
|
|
524
|
-
});
|
|
525
|
-
if (beforeCount !== count && pattern.get('elements').length < 1) {
|
|
526
|
-
variablePath.remove();
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
},
|
|
530
|
-
FunctionDeclaration: sweepFunction,
|
|
531
|
-
FunctionExpression: sweepFunction,
|
|
532
|
-
ArrowFunctionExpression: sweepFunction,
|
|
533
|
-
ImportSpecifier: sweepImport,
|
|
534
|
-
ImportDefaultSpecifier: sweepImport,
|
|
535
|
-
ImportNamespaceSpecifier: sweepImport
|
|
536
|
-
});
|
|
537
|
-
} while (count);
|
|
538
|
-
|
|
539
|
-
// Do we need the * import for react?
|
|
540
|
-
let hasReact = false;
|
|
541
|
-
|
|
542
|
-
// Mark react elements as having react
|
|
543
|
-
programPath.traverse({
|
|
544
|
-
JSXElement(path) {
|
|
545
|
-
hasReact = true;
|
|
546
|
-
}
|
|
547
|
-
});
|
|
548
|
-
if (!hasReact) {
|
|
549
|
-
// Mark all variables and functions if used
|
|
550
|
-
programPath.traverse({
|
|
551
|
-
ImportDeclaration(path) {
|
|
552
|
-
if (t.isStringLiteral(path.node.source) && path.node.source.value === 'react' && t.isImportNamespaceSpecifier(path.node.specifiers[0])) {
|
|
553
|
-
path.remove();
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
let latestTask = 0;
|
|
561
|
-
const rootRouteName = '__root';
|
|
562
|
-
const rootRouteClientName = '__root.client';
|
|
563
|
-
let nodeCache = undefined;
|
|
564
|
-
async function generator(config) {
|
|
565
|
-
console.log();
|
|
566
|
-
let first = false;
|
|
567
|
-
if (!nodeCache) {
|
|
568
|
-
first = true;
|
|
569
|
-
console.log('🔄 Generating routes...');
|
|
570
|
-
nodeCache = [];
|
|
571
|
-
} else {
|
|
572
|
-
console.log('♻️ Regenerating routes...');
|
|
573
|
-
}
|
|
574
|
-
const taskId = latestTask + 1;
|
|
575
|
-
latestTask = taskId;
|
|
576
|
-
const checkLatest = () => {
|
|
577
|
-
if (latestTask !== taskId) {
|
|
578
|
-
console.log(`- Skipping since file changes were made while generating.`);
|
|
579
|
-
return false;
|
|
580
|
-
}
|
|
581
|
-
return true;
|
|
582
|
-
};
|
|
583
|
-
const start = Date.now();
|
|
584
|
-
let routeConfigImports = [];
|
|
585
|
-
let routeConfigClientImports = [];
|
|
586
|
-
let nodesChanged = false;
|
|
587
|
-
const fileQueue = [];
|
|
588
|
-
const queueWriteFile = (filename, content) => {
|
|
589
|
-
fileQueue.push([filename, content]);
|
|
590
|
-
};
|
|
591
|
-
async function reparent(dir) {
|
|
592
|
-
let dirList;
|
|
593
|
-
try {
|
|
594
|
-
dirList = await fs.readdir(dir);
|
|
595
|
-
} catch (err) {
|
|
596
|
-
console.log();
|
|
597
|
-
console.error('TSR: Error reading the config.routesDirectory. Does it exist?');
|
|
598
|
-
console.log();
|
|
599
|
-
throw err;
|
|
600
|
-
}
|
|
601
|
-
const dirListCombo = multiSortBy(await Promise.all(dirList.map(async filename => {
|
|
602
|
-
const fullPath = path.resolve(dir, filename);
|
|
603
|
-
const stat = await fs.lstat(fullPath);
|
|
604
|
-
const ext = path.extname(filename);
|
|
605
|
-
const clientFilename = filename.replace(ext, `.client${ext}`);
|
|
606
|
-
const pathFromRoutes = path.relative(config.routesDirectory, fullPath);
|
|
607
|
-
const genPath = path.resolve(config.routeGenDirectory, pathFromRoutes);
|
|
608
|
-
const genPathNoExt = removeExt(genPath);
|
|
609
|
-
const genDir = path.resolve(genPath, '..');
|
|
610
|
-
const fileNameNoExt = removeExt(filename);
|
|
611
|
-
return {
|
|
612
|
-
filename,
|
|
613
|
-
clientFilename,
|
|
614
|
-
fileNameNoExt,
|
|
615
|
-
fullPath,
|
|
616
|
-
fullDir: dir,
|
|
617
|
-
genPath,
|
|
618
|
-
genDir,
|
|
619
|
-
genPathNoExt,
|
|
620
|
-
variable: fileToVariable(removeExt(pathFromRoutes)),
|
|
621
|
-
isDirectory: stat.isDirectory(),
|
|
622
|
-
isIndex: fileNameNoExt === 'index'
|
|
623
|
-
};
|
|
624
|
-
})), [d => d.fileNameNoExt === 'index' ? -1 : 1, d => d.fileNameNoExt, d => d.isDirectory ? 1 : -1]);
|
|
625
|
-
const reparented = [];
|
|
626
|
-
dirListCombo.forEach(async (d, i) => {
|
|
627
|
-
if (d.isDirectory) {
|
|
628
|
-
const parent = reparented.find(dd => !dd.isDirectory && dd.fileNameNoExt === d.filename);
|
|
629
|
-
if (parent) {
|
|
630
|
-
parent.childRoutesDir = d.fullPath;
|
|
631
|
-
} else {
|
|
632
|
-
reparented.push(d);
|
|
633
|
-
}
|
|
634
|
-
} else {
|
|
635
|
-
reparented.push(d);
|
|
636
|
-
}
|
|
637
|
-
});
|
|
638
|
-
return Promise.all(reparented.map(async d => {
|
|
639
|
-
if (d.childRoutesDir) {
|
|
640
|
-
const children = await reparent(d.childRoutesDir);
|
|
641
|
-
d = {
|
|
642
|
-
...d,
|
|
643
|
-
children
|
|
644
|
-
};
|
|
645
|
-
children.forEach(child => child.parent = d);
|
|
646
|
-
return d;
|
|
647
|
-
}
|
|
648
|
-
return d;
|
|
649
|
-
}));
|
|
650
|
-
}
|
|
651
|
-
const reparented = await reparent(config.routesDirectory);
|
|
652
|
-
async function buildRouteConfig(nodes, depth = 1) {
|
|
653
|
-
const children = nodes.map(async n => {
|
|
654
|
-
let node = nodeCache.find(d => d.fullPath === n.fullPath);
|
|
655
|
-
if (node) {
|
|
656
|
-
node.new = false;
|
|
657
|
-
} else {
|
|
658
|
-
node = n;
|
|
659
|
-
nodeCache.push(node);
|
|
660
|
-
if (!first) {
|
|
661
|
-
node.new = true;
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
node.version = latestTask;
|
|
665
|
-
if (node.fileNameNoExt === '__root') {
|
|
666
|
-
node.isRoot = true;
|
|
667
|
-
}
|
|
668
|
-
const routeCode = await fs.readFile(node.fullPath, 'utf-8');
|
|
669
|
-
const hashSum = crypto.createHash('sha256');
|
|
670
|
-
hashSum.update(routeCode);
|
|
671
|
-
const hash = hashSum.digest('hex');
|
|
672
|
-
node.changed = node.hash !== hash;
|
|
673
|
-
if (node.changed) {
|
|
674
|
-
nodesChanged = true;
|
|
675
|
-
node.hash = hash;
|
|
676
|
-
try {
|
|
677
|
-
// Ensure the boilerplate for the route exists
|
|
678
|
-
const code = await ensureBoilerplate(node, routeCode);
|
|
679
|
-
if (code) {
|
|
680
|
-
await fs.writeFile(node.fullPath, code);
|
|
681
|
-
}
|
|
682
|
-
let imports = [];
|
|
683
|
-
if (!node.isRoot) {
|
|
684
|
-
// Generate the isolated files
|
|
685
|
-
const transforms = await Promise.all(isolatedProperties.map(async key => {
|
|
686
|
-
let exported = false;
|
|
687
|
-
let exports = [];
|
|
688
|
-
const transformed = await isolateOptionToExport(node, routeCode, {
|
|
689
|
-
isolate: key
|
|
690
|
-
});
|
|
691
|
-
if (transformed) {
|
|
692
|
-
exports = await detectExports(transformed);
|
|
693
|
-
if (exports.includes(key)) {
|
|
694
|
-
exported = true;
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
return {
|
|
698
|
-
key,
|
|
699
|
-
exported,
|
|
700
|
-
code: transformed
|
|
701
|
-
};
|
|
702
|
-
}));
|
|
703
|
-
imports = transforms.filter(({
|
|
704
|
-
exported
|
|
705
|
-
}) => exported);
|
|
706
|
-
node.importedFiles = await Promise.all(imports.map(({
|
|
707
|
-
key,
|
|
708
|
-
code
|
|
709
|
-
}) => {
|
|
710
|
-
const importFilename = `${node.genPathNoExt}-${key}.tsx`;
|
|
711
|
-
queueWriteFile(importFilename, code);
|
|
712
|
-
return importFilename;
|
|
713
|
-
}));
|
|
714
|
-
}
|
|
715
|
-
const routeConfigCode = await generateRouteConfig(node, routeCode, imports, false);
|
|
716
|
-
const clientRouteConfigCode = await generateRouteConfig(node, routeCode, imports, true);
|
|
717
|
-
queueWriteFile(node.genPath, routeConfigCode);
|
|
718
|
-
queueWriteFile(path.resolve(node.genDir, node.clientFilename), clientRouteConfigCode);
|
|
719
|
-
} catch (err) {
|
|
720
|
-
node.hash = '';
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
routeConfigImports.push(`import { route as ${node.variable}Route } from './${removeExt(path.relative(config.routeGenDirectory, node.genPath).replace(/\\/gi, '/'))}'`);
|
|
724
|
-
routeConfigClientImports.push(`import { route as ${node.variable}Route } from './${removeExt(path.relative(config.routeGenDirectory, path.resolve(node.genDir, node.clientFilename)).replace(/\\/gi, '/'))}'`);
|
|
725
|
-
if (node.isRoot) {
|
|
726
|
-
return undefined;
|
|
727
|
-
}
|
|
728
|
-
const route = `${node.variable}Route`;
|
|
729
|
-
if (node.children?.length) {
|
|
730
|
-
const childConfigs = await buildRouteConfig(node.children, depth + 1);
|
|
731
|
-
return `${route}.addChildren([\n${spaces(depth * 4)}${childConfigs}\n${spaces(depth * 2)}])`;
|
|
732
|
-
}
|
|
733
|
-
return route;
|
|
734
|
-
});
|
|
735
|
-
return (await Promise.all(children)).filter(Boolean).join(`,\n${spaces(depth * 2)}`);
|
|
736
|
-
}
|
|
737
|
-
const routeConfigChildrenText = await buildRouteConfig(reparented);
|
|
738
|
-
routeConfigImports = multiSortBy(routeConfigImports, [d => d.includes('__root') ? -1 : 1, d => d.split('/').length, d => d.endsWith("index'") ? -1 : 1, d => d]);
|
|
739
|
-
routeConfigClientImports = multiSortBy(routeConfigClientImports, [d => d.includes('__root') ? -1 : 1, d => d.split('/').length, d => d.endsWith("index.client'") ? -1 : 1, d => d]);
|
|
740
|
-
const routeConfig = `export const routeTree = rootRoute.addChildren([\n ${routeConfigChildrenText}\n])\nexport type __GeneratedRouteConfig = typeof routeTree`;
|
|
741
|
-
const routeConfigClient = `export const routeTreeClient = rootRoute.addChildren([\n ${routeConfigChildrenText}\n]) as __GeneratedRouteConfig`;
|
|
742
|
-
const routeConfigFileContent = [routeConfigImports.join('\n'), routeConfig].join('\n\n');
|
|
743
|
-
const routeConfigClientFileContent = [`import type { __GeneratedRouteConfig } from './routeTree'`, routeConfigClientImports.join('\n'), routeConfigClient].join('\n\n');
|
|
744
|
-
if (nodesChanged) {
|
|
745
|
-
queueWriteFile(path.resolve(config.routeGenDirectory, 'routeTree.ts'), routeConfigFileContent);
|
|
746
|
-
queueWriteFile(path.resolve(config.routeGenDirectory, 'routeTree.client.ts'), routeConfigClientFileContent);
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
// Do all of our file system manipulation at the end
|
|
750
|
-
await fs.mkdir(config.routeGenDirectory, {
|
|
751
|
-
recursive: true
|
|
752
|
-
});
|
|
753
|
-
if (!checkLatest()) return;
|
|
754
|
-
await Promise.all(fileQueue.map(async ([filename, content]) => {
|
|
755
|
-
await fs.ensureDir(path.dirname(filename));
|
|
756
|
-
const exists = await fs.pathExists(filename);
|
|
757
|
-
let current = '';
|
|
758
|
-
if (exists) {
|
|
759
|
-
current = await fs.readFile(filename, 'utf-8');
|
|
760
|
-
}
|
|
761
|
-
if (current !== content) {
|
|
762
|
-
await fs.writeFile(filename, content);
|
|
763
|
-
}
|
|
764
|
-
}));
|
|
765
|
-
if (!checkLatest()) return;
|
|
766
|
-
const allFiles = await getAllFiles(config.routeGenDirectory);
|
|
767
|
-
if (!checkLatest()) return;
|
|
768
|
-
const removedNodes = [];
|
|
769
|
-
nodeCache = nodeCache.filter(d => {
|
|
770
|
-
if (d.version !== latestTask) {
|
|
771
|
-
removedNodes.push(d);
|
|
772
|
-
return false;
|
|
773
|
-
}
|
|
774
|
-
return true;
|
|
775
|
-
});
|
|
776
|
-
const newNodes = nodeCache.filter(d => d.new);
|
|
777
|
-
const updatedNodes = nodeCache.filter(d => !d.new && d.changed);
|
|
778
|
-
const unusedFiles = allFiles.filter(d => {
|
|
779
|
-
if (d === path.resolve(config.routeGenDirectory, 'routeTree.ts') || d === path.resolve(config.routeGenDirectory, 'routeTree.client.ts')) {
|
|
780
|
-
return false;
|
|
781
|
-
}
|
|
782
|
-
let node = nodeCache.find(n => n.genPath === d || path.resolve(n.genDir, n.clientFilename) === d || n.importedFiles?.includes(d));
|
|
783
|
-
return !node;
|
|
784
|
-
});
|
|
785
|
-
await Promise.all(unusedFiles.map(d => {
|
|
786
|
-
fs.remove(d);
|
|
787
|
-
}));
|
|
788
|
-
console.log(`🌲 Processed ${nodeCache.length} routes in ${Date.now() - start}ms`);
|
|
789
|
-
if (newNodes.length || updatedNodes.length || removedNodes.length) {
|
|
790
|
-
if (newNodes.length) {
|
|
791
|
-
console.log(`🥳 Added ${newNodes.length} new routes`);
|
|
792
|
-
}
|
|
793
|
-
if (updatedNodes.length) {
|
|
794
|
-
console.log(`✅ Updated ${updatedNodes.length} routes`);
|
|
795
|
-
}
|
|
796
|
-
if (removedNodes.length) {
|
|
797
|
-
console.log(`🗑 Removed ${removedNodes.length} unused routes`);
|
|
798
|
-
}
|
|
799
|
-
} else {
|
|
800
|
-
console.log(`🎉 No changes were found. Carry on!`);
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
function getAllFiles(dir) {
|
|
804
|
-
return new Promise((resolve, reject) => {
|
|
805
|
-
const excludeDirFilter = through2.obj(function (item, enc, next) {
|
|
806
|
-
if (!item.stats.isDirectory()) this.push(item);
|
|
807
|
-
next();
|
|
808
|
-
});
|
|
809
|
-
const items = [];
|
|
810
|
-
klaw(dir).pipe(excludeDirFilter).on('data', item => items.push(item.path)).on('error', err => reject(err)).on('end', () => resolve(items));
|
|
811
|
-
});
|
|
812
|
-
}
|
|
813
|
-
function fileToVariable(d) {
|
|
814
|
-
return d.split('/').map((d, i) => i > 0 ? capitalize(d) : d).join('').replace(/([^a-zA-Z0-9]|[\.])/gm, '');
|
|
815
|
-
}
|
|
816
|
-
function removeExt(d) {
|
|
817
|
-
return d.substring(0, d.lastIndexOf('.')) || d;
|
|
818
|
-
}
|
|
819
|
-
function spaces(d) {
|
|
820
|
-
return Array.from({
|
|
821
|
-
length: d
|
|
822
|
-
}).map(() => ' ').join('');
|
|
823
|
-
}
|
|
824
|
-
function multiSortBy(arr, accessors = [d => d]) {
|
|
825
|
-
return arr.map((d, i) => [d, i]).sort(([a, ai], [b, bi]) => {
|
|
826
|
-
for (const accessor of accessors) {
|
|
827
|
-
const ao = accessor(a);
|
|
828
|
-
const bo = accessor(b);
|
|
829
|
-
if (typeof ao === 'undefined') {
|
|
830
|
-
if (typeof bo === 'undefined') {
|
|
831
|
-
continue;
|
|
832
|
-
}
|
|
833
|
-
return 1;
|
|
834
|
-
}
|
|
835
|
-
if (ao === bo) {
|
|
836
|
-
continue;
|
|
837
|
-
}
|
|
838
|
-
return ao > bo ? 1 : -1;
|
|
839
|
-
}
|
|
840
|
-
return ai - bi;
|
|
841
|
-
}).map(([d]) => d);
|
|
842
|
-
}
|
|
843
|
-
function capitalize(s) {
|
|
844
|
-
if (typeof s !== 'string') return '';
|
|
845
|
-
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
async function generate(config) {
|
|
849
|
-
try {
|
|
850
|
-
await generator(config);
|
|
851
|
-
process.exit(0);
|
|
852
|
-
} catch (err) {
|
|
853
|
-
console.error(err);
|
|
854
|
-
process.exit(1);
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
async function watch() {
|
|
859
|
-
const configWatcher = chokidar.watch(path.resolve(process.cwd(), 'tsr.config.js'));
|
|
860
|
-
let watcher = new chokidar.FSWatcher();
|
|
861
|
-
const generatorWatcher = async () => {
|
|
862
|
-
const config = await getConfig();
|
|
863
|
-
watcher.close();
|
|
864
|
-
console.log(`TSR: Watching routes (${config.routesDirectory})...`);
|
|
865
|
-
watcher = chokidar.watch(config.routesDirectory);
|
|
866
|
-
watcher.on('ready', async () => {
|
|
867
|
-
try {
|
|
868
|
-
await generator(config);
|
|
869
|
-
} catch (err) {
|
|
870
|
-
console.error(err);
|
|
871
|
-
console.log();
|
|
872
|
-
}
|
|
873
|
-
const handle = async () => {
|
|
874
|
-
try {
|
|
875
|
-
await generator(config);
|
|
876
|
-
} catch (err) {
|
|
877
|
-
console.error(err);
|
|
878
|
-
console.log();
|
|
879
|
-
}
|
|
880
|
-
};
|
|
881
|
-
watcher.on('change', handle);
|
|
882
|
-
watcher.on('add', handle);
|
|
883
|
-
watcher.on('addDir', handle);
|
|
884
|
-
watcher.on('unlink', handle);
|
|
885
|
-
watcher.on('unlinkDir', handle);
|
|
886
|
-
});
|
|
887
|
-
};
|
|
888
|
-
configWatcher.on('ready', generatorWatcher);
|
|
889
|
-
configWatcher.on('change', generatorWatcher);
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
main();
|
|
893
|
-
function main() {
|
|
894
|
-
yargs.scriptName('tsr').usage('$0 <cmd> [args]').command('generate', 'Generate the routes for a project', async argv => {
|
|
895
|
-
const config = await getConfig();
|
|
896
|
-
await generate(config);
|
|
897
|
-
}).command('watch', 'Continuously watch and generate the routes for a project', async argv => {
|
|
898
|
-
watch();
|
|
899
|
-
}).help().argv;
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
export { main };
|
|
903
|
-
//# sourceMappingURL=index.js.map
|