Skip to content

Commit 8119f27

Browse files
author
Björn Büttner
committed
introduce config usage
1 parent 0a0a1ca commit 8119f27

12 files changed

+148
-94
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
.idea
22
node_modules
3+
src/*.js
4+
/tsconfig.tsbuildinfo

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ interface Configuration {
2121
},
2222
routes?: {
2323
build?: true,
24+
type?: 'tsx'|'jsx',
2425
},
2526
htmlMinify?: {
2627
collapseBooleanAttributes?: boolean,
@@ -45,6 +46,10 @@ interface Configuration {
4546
}
4647
```
4748

49+
If you prefer using a cli format, `--{group}.{setting}=abc` will set string settings to abc, while booleans can be flipped by just using `--{group}.{setting}`. For example for api-bench, that uses the defaults, you can set the domain by using `--sitemap.domain=idrinth-api-ben.ch`. The setting `fileFinder.overridePathMappings` can not be configured via the cli.
50+
51+
Default options are overwritten first by the file based settings and then by the command line settings.
52+
4853
# Setup
4954

5055
You will need to do three things for the complete package:

bin/generate-folders.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env node
22
import generate from '../src/generate-folders.js';
3-
import configuration from '../src/configuration';
3+
import configuration from '../src/configuration.js';
44

5-
generate(process.cwd(), configuration(process.cwd()));
5+
generate(process.cwd(), configuration(process.cwd(), process.argv));

bin/generate-routes.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env node
22
import generate from '../src/generate-routes.js';
3-
import configuration from '../src/configuration';
3+
import configuration from '../src/configuration.js';
44

5-
generate(process.cwd(), configuration(process.cwd()));
5+
generate(process.cwd(), configuration(process.cwd(), process.argv));

package-lock.json

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

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"name": "@idrinth/react-file-based-routes",
33
"version": "1.0.0",
4-
"description": "",
4+
"description": "A simple file based routing library, that does not force itself on you.",
5+
"type": "module",
56
"bin": {
67
"react-file-based-routes-generate-routes": "bin/generate-routes.js",
78
"react-file-based-routes-generate-folders": "bin/generate-folders.js",
@@ -16,12 +17,14 @@
1617
"typescript": "^5.3.3"
1718
},
1819
"dependencies": {
19-
"app-root-path": "^3.1.0",
2020
"astring": "^1.8.6",
2121
"estree-walker": "^3.0.3",
2222
"fast-glob": "^3.3.2",
2323
"html-minifier": "^4.0.0",
2424
"magic-string": "^0.30.5",
2525
"react": "^18.2.0"
26+
},
27+
"scripts": {
28+
"tsc": "tsc -p tsconfig.json"
2629
}
2730
}

src/configuration.ts

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1-
import {readFileSync, existsSync} from 'fs';
1+
import {
2+
readFileSync,
3+
existsSync,
4+
} from 'fs';
25

36
export interface Configuration {
47
sitemap: {
58
domain: string,
69
build: boolean,
710
},
811
routes: {
9-
build: boolean
12+
build: boolean,
13+
type: 'tsx'|'jsx',
14+
overridePathMappings: {
15+
[filesystemPath: string]: string,
16+
},
1017
},
1118
htmlMinify: {
1219
collapseBooleanAttributes: boolean,
@@ -22,36 +29,34 @@ export interface Configuration {
2229
},
2330
fileFinder: {
2431
fileName: string,
25-
overridePathMappings: {
26-
[filesystemPath: string]: string,
27-
},
2832
pagesRoot: string,
2933
distJSRoot: string,
3034
},
3135
}
3236

33-
export default (cwd: string): Configuration => {
37+
export default (cwd: string, cliArguments: string[]): Configuration => {
3438
const config = {
3539
sitemap: {
3640
build: true,
3741
domain: '',
3842
},
3943
routes: {
4044
build: true,
45+
type: 'tsx',
46+
overridePathMappings: {
47+
home: '/',
48+
'not-found': '*',
49+
},
4150
},
4251
htmlMinify: {
4352
collapseBooleanAttributes: true,
44-
conservativeCollapse: true,
53+
conservativeCollapse: false,
4554
collapseWhitespace: true,
4655
removeAttributeQuotes: true,
4756
removeComments: true,
4857
},
4958
fileFinder: {
5059
fileName: 'index.tsx',
51-
overridePathMappings: {
52-
home: '/',
53-
'not-found': '*',
54-
},
5560
pagesRoot: 'src/pages',
5661
distJSRoot: 'dist/assets',
5762
},
@@ -66,7 +71,7 @@ export default (cwd: string): Configuration => {
6671
const userconfig = JSON.parse(readFileSync(configFile, 'utf-8'));
6772
if (typeof userconfig === 'object') {
6873
for (const prop of Object.keys(config)) {
69-
if (typeof userconfig[prop] === 'object') {
74+
if (typeof userconfig[prop] === 'object' && typeof config[prop] === 'object') {
7075
for (const setting of Object.keys(config[prop])) {
7176
if (typeof config[prop][setting] === typeof userconfig[prop][setting]) {
7277
config[prop][setting] = userconfig[prop][setting];
@@ -76,6 +81,21 @@ export default (cwd: string): Configuration => {
7681
}
7782
}
7883
}
84+
for (const param of cliArguments) {
85+
if (param.startsWith('--')) {
86+
const [setting, value,] = param.substring(2).split('=');
87+
const [group, detail,] = setting.split('.');
88+
if (group && typeof config[group] === 'object') {
89+
if (detail) {
90+
if (typeof config[group][detail] === 'boolean') {
91+
config[group][detail] = !config[group][detail];
92+
} else if(typeof config[group][detail] === 'string' && value) {
93+
config[group][detail] = value;
94+
}
95+
}
96+
}
97+
}
98+
}
7999
if (config.sitemap.build && !config.sitemap.domain) {
80100
throw new Error('sitemap.domain must be set if a sitemap should be build.');
81101
}

src/generate-folders.ts

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,34 @@
11
import {
22
readdirSync,
33
readFileSync,
4-
mkdirSync,
5-
writeFileSync,
64
} from 'fs';
75
import minifier from 'html-minifier';
8-
import {Configuration} from './configuration.ts';
6+
import {Configuration} from './configuration.js';
7+
import writeIndexHtml from "./write-index-html.js";
98

109
export default (cwd: string, configuration: Configuration) => {
11-
const template = readFileSync(cwd + '/dist/index.html', 'utf8',);
10+
const template = (() => {
11+
const data = readFileSync(cwd + '/dist/index.html', 'utf8',);
12+
if (configuration.fileBuilder.minifyPages) {
13+
return minifier.minify(data, configuration.htmlMinify);
14+
}
15+
return data;
16+
})();
17+
const matcher = new RegExp(`${configuration.fileFinder.distJSRoot}/(.*?)${configuration.fileFinder.fileName}`);
18+
const fileName = configuration.fileFinder.fileName.replace(/\.tsx$/, '-');
1219

13-
for (const file of readdirSync(cwd + configuration.fileFinder.distJSRoot, {encoding: 'utf8', recursive: true})) {
14-
if (file.endsWith('.js')) {
15-
const content = readFileSync(cwd + configuration.fileFinder.distJSRoot + '/' + file, 'utf8',);
16-
const res = content.match(/src\/pages\/(.*?)\/index.tsx/);
17-
if (res && res[1] && res[1] !== 'home' && res[1] !== 'not-found') {
18-
mkdirSync('./dist/' + res[1], {recursive: true},);
19-
writeFileSync(
20-
cwd + '/dist/' + res[1] + '/index.html',
21-
minifier.minify(
22-
template.replace(/<\/head>/, `<script src="/assets/${file}" type="module"></script></head>`),
23-
configuration.htmlMinify,
24-
),
25-
);
26-
} else if (res && res[1] === 'home') {
27-
writeFileSync(
28-
cwd + '/dist/index.html',
29-
minifier.minify(
30-
template.replace(/<\/head>/, `<script src="/assets/${file}" type="module"></script></head>`),
31-
configuration.htmlMinify,
32-
),
33-
);
20+
for (const file of readdirSync(cwd + '/' + configuration.fileFinder.distJSRoot, {encoding: 'utf8', recursive: true})) {
21+
if (file.endsWith('.js') && file.startsWith(fileName)) {
22+
const content = readFileSync(cwd + '/' + configuration.fileFinder.distJSRoot + '/' + file, 'utf8',);
23+
const res = matcher.exec(content);
24+
if (res && res[1]) {
25+
if (typeof configuration.routes.overridePathMappings[res[1]] === 'string') {
26+
if (configuration.routes.overridePathMappings[res[1]] !== '*') {
27+
writeIndexHtml(cwd, file, configuration.routes.overridePathMappings[res[1]], template, configuration);
28+
}
29+
} else {
30+
writeIndexHtml(cwd, file, `/${ res[1] }/`, template, configuration);
31+
}
3432
}
3533
}
3634
}

src/generate-routes.ts

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,67 @@
11
import {readdirSync, statSync, writeFileSync,} from 'fs';
22
import paddedDate from './padded-date.js';
3-
import {Configuration} from "./configuration.ts";
3+
import {Configuration} from "./configuration.js";
44

55
export default (cwd: string, configuration: Configuration) => {
66
let xml = '<?xml version="1.0" encoding="UTF-8"?>';
77
let jsx = 'import React, {\n' +
8-
' lazy,\n' +
9-
' ReactElement,\n' +
10-
' Suspense,\n' +
11-
' ElementType,\n' +
12-
'} from \'react\';\n' +
13-
'export default const routes = (Loader) => [';
8+
' lazy,\n' +
9+
' Suspense,\n' +
10+
'} from \'react\';\n' +
11+
'export default (Loader) => [';
12+
let tsx = 'import React, {\n' +
13+
' lazy,\n' +
14+
' Suspense,\n' +
15+
' ElementType,\n' +
16+
'} from \'react\';\n' +
17+
'export default (Loader: ElementType) => [';
1418
xml += '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
15-
for (const file of readdirSync(cwd + configuration.fileFinder.pagesRoot, {encoding: 'utf8', recursive: true})) {
19+
for (const file of readdirSync(cwd + '/' + configuration.fileFinder.pagesRoot, {encoding: 'utf8', recursive: true})) {
1620
if (file.endsWith('/' + configuration.fileFinder.fileName) || file.endsWith('\\' + configuration.fileFinder.fileName)) {
17-
const mtime = statSync(cwd + configuration.fileFinder.pagesRoot + file).mtime;
21+
const mtime = statSync(cwd + '/' + configuration.fileFinder.pagesRoot + '/' + file).mtime;
1822
const changed = paddedDate(mtime);
1923
const path = file.replace(/\\/ug, '/').substring(0, file.length - configuration.fileFinder.fileName.length - 1);
20-
if (typeof configuration.fileFinder.overridePathMappings[path] === 'string') {
21-
if (configuration.fileFinder.overridePathMappings[path] !== '*') {
22-
xml += `<url><loc>https://${ configuration.sitemap.domain }${configuration.fileFinder.overridePathMappings[path]}/</loc>`;
24+
if (typeof configuration.routes.overridePathMappings[path] === 'string') {
25+
if (configuration.routes.overridePathMappings[path] !== '*') {
26+
xml += `<url><loc>https://${ configuration.sitemap.domain }${configuration.routes.overridePathMappings[path]}/</loc>`;
2327
xml += `<lastmod>${changed}</lastmod></url>`;
2428
}
25-
jsx += `(() => {
26-
const LazyElement = lazy(() => import(
29+
jsx += '\n ' + `(() => {
30+
const LazyElement = lazy(() => import(
2731
\`./pages/${path}/index.tsx\`,
2832
),);
2933
return {
30-
path: '${ configuration.fileFinder.overridePathMappings[path] }',
34+
path: '${ configuration.routes.overridePathMappings[path] }',
35+
exact: true,
36+
element: <Suspense fallback={<Loader/>}><LazyElement/></Suspense>,
37+
};
38+
})(),`;
39+
tsx += `(() => {
40+
const LazyElement = lazy(() => import(
41+
\`./pages/${path}/index.tsx\`,
42+
),);
43+
return {
44+
path: '${ configuration.routes.overridePathMappings[path] }',
3145
exact: true,
3246
element: <Suspense fallback={<Loader/>}><LazyElement/></Suspense>,
3347
};
3448
})(),`;
3549
} else {
36-
xml += `<url><loc>https://${ configuration.sitemap.domain }/${ path }/</loc>`;
37-
xml += `<lastmod>${ changed }</lastmod></url>`;
38-
jsx += `(() => {
39-
const LazyElement = lazy(() => import(
40-
\`./pages/${ path }/index.tsx\`,
50+
xml += `<url><loc>https://${configuration.sitemap.domain}/${path}/</loc>`;
51+
xml += `<lastmod>${changed}</lastmod></url>`;
52+
jsx += '\n ' + `(() => {
53+
const LazyElement = lazy(() => import(
54+
\`./pages/${path}/index.tsx\`,
55+
),);
56+
return {
57+
path: '/${path}/',
58+
exact: true,
59+
element: <Suspense fallback={<Loader/>}><LazyElement/></Suspense>,
60+
};
61+
})(),`;
62+
tsx += '\n ' + `(() => {
63+
const LazyElement = lazy(() => import(
64+
\'./pages/${ path }/index.tsx\',
4165
),);
4266
return {
4367
path: '/${ path }/',
@@ -49,11 +73,15 @@ export default (cwd: string, configuration: Configuration) => {
4973
}
5074
}
5175
xml += '</urlset>';
52-
jsx += '];'
76+
jsx += '\n];'
77+
tsx += '\n];'
5378

54-
if (configuration.routes.build) {
79+
if (configuration.routes.build && configuration.routes.type === 'jsx') {
5580
writeFileSync(cwd + '/src/routes.jsx', jsx);
5681
}
82+
if (configuration.routes.build && configuration.routes.type === 'tsx') {
83+
writeFileSync(cwd + '/src/routes.tsx', tsx);
84+
}
5785
if (configuration.sitemap.build) {
5886
writeFileSync(cwd + '/public/sitemap.xml', xml);
5987
}

src/padded-date.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export default (date: Date) => {
1+
export default (date: Date) : string=> {
22
let data = `${date.getUTCFullYear()}-`;
33
if (date.getUTCMonth() < 9) {
44
data += '0';

0 commit comments

Comments
 (0)