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