Skip to content

Commit b587309

Browse files
authored
feat(routeinfo): pulled complete roueInfo insto route-discovery phase (#252)
1 parent be48c03 commit b587309

File tree

16 files changed

+134
-79
lines changed

16 files changed

+134
-79
lines changed

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"cSpell.words": ["Scully", "Scullyio", "ngcontent"],
2+
"cSpell.words": ["Scully", "Scullyio", "ngcontent", "routify", "slugify"],
33
"peacock.affectActivityBar": true,
44
"peacock.affectTitleBar": true,
55
"peacock.affectStatusBar": true,

blog/page-1.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ title: My first page
33
author: Sander Elias
44
publish date: 2019-11-26
55
slug: look at_my-urls Cool
6+
slugs:
7+
- 'page-1'
68
description: This is the first demo page in this sample.
79
---
810

blog/page-2.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ published: false
66
description: 'This is the second demo page in this sample.'
77
slugs:
88
- ___UNPUBLISHED___k5nhcflm_SJwD4Z0QDrIHg1PGHo2mrfLZE8sfUsPy
9+
- page-2
910
---
1011

1112
# Page 2

blog/page-3.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ title: My third page
33
author: Sander Elias
44
publish date: 2019-11-28
55
description: At this point, I should write something different in here.
6+
slug: My third page,
7+
slugs:
8+
- page-3
69
---
710

811
# Page 3

package-lock.json

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

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131
"@angular/platform-browser-dynamic": "~9.0.0-rc.7",
3232
"@angular/platform-server": "~9.0.0-rc.7",
3333
"@angular/router": "~9.0.0-rc.7",
34+
"@types/minimatch": "^3.0.3",
3435
"jsdom": "^15.2.1",
36+
"minimatch": "^3.0.4",
3537
"rxjs": "^6.5.3",
3638
"semver": "^6.3.0",
3739
"tslib": "^1.10.0",

projects/scullyio/ng-lib/src/lib/route-service/scully-routes.service.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface ScullyRoute {
99
slugs?: string[];
1010
published?: boolean;
1111
slug?: string;
12+
sourceFile?: string;
1213
[prop: string]: any;
1314
}
1415

@@ -27,6 +28,7 @@ export class ScullyRoutesService {
2728
}),
2829
/** filter out all non-array results */
2930
filter(routes => Array.isArray(routes)),
31+
map(this.cleanDups),
3032
shareReplay({refCount: false, bufferSize: 1})
3133
);
3234
available$ = this.allRoutes$.pipe(
@@ -66,6 +68,12 @@ export class ScullyRoutesService {
6668
);
6769
}
6870

71+
cleanDups(routes: ScullyRoute[]) {
72+
const m = new Map<string, ScullyRoute>();
73+
routes.forEach(r => m.set(r.sourceFile || r.route, r));
74+
return [...m.values()];
75+
}
76+
6977
reload(): void {
7078
this.refresh.next();
7179
}

projects/scullyio/ng-lib/src/lib/utils/fromMutationObserver.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import {Observable} from 'rxjs';
22
/**
33
* Returns an observable that fires a mutation when the domMutationObserves does that.
44
* if flattens the mutations to make handling easier, so you only get 1 mutationRecord at a time.
5-
* @param elm the elm to obse with a mutationObserver
6-
* @param config the config for the mutationobserver
5+
* @param elm the elm to observe with a mutationObserver
6+
* @param config the config for the mutation-observer
77
*/
88
export function fromMutationObserver(
99
elm: HTMLElement,

scully/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {RouteTypes, ScullyConfig} from './utils/interfacesandenums';
77
import {replaceFirstRouteParamWithVal} from './utils/replaceFirstRouteParamWithVal';
88
import {routeSplit} from './utils/routeSplit';
99
import {startScully} from './utils/startup';
10+
import {ContentMetaData} from './renderPlugins/content-render-utils/readFileAndCheckPrePublishSlug';
1011

1112
export {
1213
startScully,
@@ -20,4 +21,5 @@ export {
2021
configValidator,
2122
httpGetJson,
2223
scullyConfig,
24+
ContentMetaData,
2325
};

scully/pluginManagement/pluginRepository.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const registerPlugin = (
3535
type: PluginTypes,
3636
name: string,
3737
plugin: any,
38-
validator = async () => [],
38+
validator = async (config?: any) => [],
3939
{replaceExistingPlugin = false} = {}
4040
) => {
4141
if (!['router', 'render', 'fileHandler'].includes(type)) {
Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,42 @@
11
import {readFileSync, writeFileSync} from 'fs';
22
import {stringify} from 'yamljs';
3-
import {HandledRoute} from '../../routerPlugins/addOptionalRoutesPlugin';
43
import {randomString} from '../../utils/randomString';
54
const fm = require('front-matter');
65

7-
export async function readFileAndCheckPrePublishSlug(file, route: HandledRoute) {
6+
export interface ContentMetaData {
7+
author?: string;
8+
published?: boolean;
9+
slug?: string;
10+
'publish date'?: Date;
11+
slugs?: string[];
12+
title?: string;
13+
[key: string]: any;
14+
}
15+
16+
export async function readFileAndCheckPrePublishSlug(file) {
817
const prependString = '___UNPUBLISHED___';
918
const createSlug = () => `${prependString}${Date.now().toString(36)}_${randomString(32)}`;
1019
const originalFile = readFileSync(file, 'utf-8');
11-
const {attributes: meta, body: fileContent} = fm(originalFile);
20+
const {attributes: meta, body: fileContent}: {attributes: ContentMetaData; body: string} = fm(originalFile);
21+
let prePublished = false;
1222
if (meta.hasOwnProperty('published') && meta.published === false) {
1323
/** this post needs an pre-publish slug */
1424
const slugs = Array.isArray(meta.slugs) ? meta.slugs : [];
15-
let slug = slugs.find((sl: string) => sl.startsWith(prependString));
16-
if (!slug) {
25+
let unPublishedSlug = slugs.find((sl: string) => sl.startsWith(prependString));
26+
if (!unPublishedSlug) {
1727
/** there is no prepub slug so create one and update the file */
18-
slug = createSlug();
19-
meta.slugs = slugs.concat(slug);
28+
unPublishedSlug = createSlug();
29+
meta.slugs = slugs.concat(unPublishedSlug);
2030
/** string literal, don't format "correctly" or things will break ;) */
2131
const newFile = `---
2232
${stringify(meta)}
2333
---
2434
${fileContent}`;
2535
writeFileSync(file, newFile);
2636
}
27-
/** replace the route with the prepub slug */
28-
const updatedRoute = replaceRouteWithSlug(route.route, slug);
29-
route.route = updatedRoute;
30-
} else if (typeof meta.slug === 'string') {
31-
route.route = replaceRouteWithSlug(
32-
// TODO better handling of url/filename escaping
33-
route.route,
34-
encodeURIComponent(
35-
meta.slug
36-
.trim()
37-
.split('/')
38-
.join('_')
39-
.split(' ')
40-
.join('_')
41-
.split('?')
42-
.join('_')
43-
)
44-
);
37+
/** overwrite slug from file with prepub only in memory. we don't want a file with the original slug name now. */
38+
meta.slug = unPublishedSlug;
39+
prePublished = true;
4540
}
46-
return {meta, fileContent};
47-
}
48-
49-
function replaceRouteWithSlug(route: string, slug: string) {
50-
const lastPart = route.split('/').pop();
51-
return route.replace(lastPart, slug);
41+
return {meta, fileContent, prePublished};
5242
}

scully/renderPlugins/contentRenderPlugin.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@ export async function contentRenderPlugin(html: string, route: HandledRoute) {
1717
const file = route.templateFile;
1818
try {
1919
const extension = file.split('.').pop();
20-
const {meta, fileContent} = await readFileAndCheckPrePublishSlug(file, route);
20+
const {fileContent} = await readFileAndCheckPrePublishSlug(file);
2121
// TODO: create additional "routes" for every slug
22-
route.data = {...route.data, ...meta};
2322
const attr = getIdAttrName(
2423
html
2524
.split('<scully-content')[1]
Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,70 @@
11
import {readdir} from 'fs';
22
import {basename, extname, join} from 'path';
3-
import {configValidator, registerPlugin} from '../pluginManagement/pluginRepository';
3+
import {registerPlugin} from '../pluginManagement/pluginRepository';
4+
import {readFileAndCheckPrePublishSlug} from '../renderPlugins/content-render-utils/readFileAndCheckPrePublishSlug';
45
import {scullyConfig} from '../utils/config';
56
import {RouteTypeContentFolder} from '../utils/interfacesandenums';
67
import {log, yellow} from '../utils/log';
78
import {HandledRoute} from './addOptionalRoutesPlugin';
89

910
export async function contentFolderPlugin(
10-
route: string,
11+
angularRoute: string,
1112
conf: RouteTypeContentFolder
1213
): Promise<HandledRoute[]> {
13-
const parts = route.split('/');
14+
const parts = angularRoute.split('/');
1415
/** for now, just handle the First parameter. Not sure if/how we can handle multiple ones. */
1516
const param = parts.filter(p => p.startsWith(':')).map(id => id.slice(1))[0];
1617
const paramConfig = conf[param];
1718
if (!paramConfig) {
18-
console.error(`missing config for parameters (${param}) in route: ${route}. Skipping`);
19+
console.error(`missing config for parameters (${param}) in route: ${angularRoute}. Skipping`);
1920
return [];
2021
}
21-
const baseRoute = route.split(':' + param)[0];
22+
const baseRoute = angularRoute.split(':' + param)[0];
2223
const path = join(scullyConfig.homeFolder, paramConfig.folder);
2324
log(`Finding files in folder "${yellow(path)}"`);
2425
const files = await new Promise<string[]>(resolve => readdir(path, (err, data) => resolve(data)));
25-
return files.map<HandledRoute>(file => {
26-
const ext = extname(file);
27-
const base = basename(file, ext);
28-
return {
29-
route: `${baseRoute}${base}`,
26+
const handledRoutes: HandledRoute[] = [];
27+
for (const sourceFile of files) {
28+
const ext = extname(sourceFile);
29+
const templateFile = join(path, sourceFile);
30+
const base = basename(sourceFile, ext);
31+
const routify = frag => `${baseRoute}${slugify(frag)}`;
32+
const {meta, prePublished} = await readFileAndCheckPrePublishSlug(templateFile);
33+
const handledRoute: HandledRoute = {
34+
route: routify(meta.slug || base),
3035
type: conf.type,
31-
templateFile: join(path, file),
36+
templateFile,
37+
data: {...meta, sourceFile},
3238
};
33-
});
39+
handledRoutes.push(handledRoute);
40+
if (!prePublished && Array.isArray(meta.slugs)) {
41+
/** also add routes for all available slugs */
42+
meta.slugs
43+
.filter(slug => typeof slug === 'string')
44+
.map(routify)
45+
.forEach(route => handledRoutes.push({...handledRoute, route}));
46+
}
47+
}
48+
return handledRoutes;
49+
}
50+
51+
export function slugify(frag: string): string {
52+
return encodeURIComponent(
53+
frag
54+
.trim()
55+
.split('/')
56+
.join('_')
57+
.split(' ')
58+
.join('_')
59+
.split('?')
60+
.join('_')
61+
);
3462
}
3563

3664
// TODO actual validation of the config
37-
contentFolderPlugin[configValidator] = async conf => {
65+
const configValidator = async conf => {
3866
// return [yellow('all seems ok')];
3967
return [];
4068
};
4169

42-
registerPlugin('router', 'contentFolder', contentFolderPlugin);
70+
registerPlugin('router', 'contentFolder', contentFolderPlugin, configValidator);

scully/scully.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ const {argv: options} = yargs.option('port', {
3030
description: 'The port to run on',
3131
});
3232

33+
const {removeStaticDist} = yargs
34+
.boolean('RSD')
35+
.default('RSD', false)
36+
.alias('RSD', 'removeStaticDist')
37+
.describe('RSD', 'Use this flag to remove the Scully outfolder before starting').argv;
38+
3339
if (process.argv.includes('version')) {
3440
const {version} = JSON.parse(readFileSync(join(__dirname, './package.json')).toString());
3541
console.log('version:', version);
@@ -53,7 +59,7 @@ if (process.argv.includes('version')) {
5359
} else {
5460
const folder = join(scullyConfig.homeFolder, scullyConfig.distFolder);
5561
/** copy in current build artifacts */
56-
await moveDistAngular(folder, scullyConfig.outDir, {removeStaticDist: true, reset: false});
62+
await moveDistAngular(folder, scullyConfig.outDir, {removeStaticDist, reset: false});
5763

5864
/** server ports already in use? */
5965
const isTaken = await isPortTaken(scullyConfig.staticport);

scully/systemPlugins/storeRoutes.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,31 @@ import {log, logError, yellow} from '../utils/log';
88
const routesFileName = '/assets/scully-routes.json';
99

1010
export async function storeRoutes(routes: HandledRoute[]) {
11-
// TODO: do something with meta-data.
12-
let appFile;
13-
let staticFile;
11+
const files = [
12+
/** in the angular source folder */
13+
join(scullyConfig.homeFolder, scullyConfig.sourceRoot, routesFileName),
14+
/** in the scully outfolder */
15+
join(scullyConfig.outDir, routesFileName),
16+
/** in the angular dist folder */
17+
join(scullyConfig.homeFolder, scullyConfig.distFolder, routesFileName),
18+
];
1419
try {
1520
const jsonResult = JSON.stringify(routes.map(r => ({route: r.route || '/', ...r.data})));
16-
appFile = join(scullyConfig.sourceRoot, `${routesFileName}`);
17-
createFolderFor(appFile);
18-
writeFileSync(appFile, jsonResult);
19-
staticFile = join(scullyConfig.outDir, routesFileName);
20-
createFolderFor(staticFile);
21-
writeFileSync(staticFile, jsonResult);
22-
log(`Route list created in files:
23-
${yellow(appFile)}
24-
${yellow(staticFile)}`);
21+
const write = file => {
22+
createFolderFor(file);
23+
writeFileSync(file, jsonResult);
24+
};
25+
files.forEach(write);
26+
log(`Route list created in files:${files.map(
27+
f => `
28+
"${yellow(f)}"`
29+
)}
30+
`);
2531
} catch {
26-
logError(`Could not write routes to files:
27-
${yellow(appFile)}
28-
${yellow(staticFile)}`);
32+
logError(`Could not write routes to files:${files.map(
33+
f => `
34+
"${yellow(f)}"`
35+
)}
36+
`);
2937
}
3038
}

0 commit comments

Comments
 (0)