Skip to content

Commit 3445f3a

Browse files
d-koppenhagenjorgeucano
authored andcommitted
refactor/cleanup: scully schematics (#177)
* refactor(scully): cleaned up schematic `scully` - use existing utils to read and write `package.json` config parts - split logic into smaller chainable functions - provide aditional tests - remove unused schema option `project` * refactor(scully): use consistent id's and titles * refactor(scully): use context.logger instead of console.log * chore(scully): remove unused dependency chalk * refactor(scully): replace all log with context.logger logs * refactor(scully): outsource yaml converter * style(scully): fix typo * refactor(scully): use `post` schematic for initial post avoid duplicat / mismatching code * refactor(scully): use getFileContents utils function * refactor(scully): cleanup markdown schematic including tests * refactor(scully): use normalized paths * fix(scully): use correct dasherized module path * test(scully): add more tests for markdown schematic * refactor(scully): camelize slug and use correct defaults * fix(scully): remove unused title prop from schema * refactor(scully): remove unused options from methods * fix(scully): set correct defaults calling other schematic * test(scully): update test for blog schematic * test(scully): test blog schematic with specific routingScope * test(scully): verify existing post won't be overridden * test(scully): verify blog schematic is not called ...when disabling it via options * fix(scully): set correct projectRoot src path ...and test it * test(scully): test fail when no angular.json * test(scully): fix error check in test * test(scully): add test when appModule not exists * feat(scully): remove blog promt for ng-add * test(scully): fix test after merge
1 parent bd63d98 commit 3445f3a

24 files changed

Lines changed: 460 additions & 195 deletions

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@
5858
"@types/request": "^2.48.4",
5959
"@types/yargs": "^13.0.3",
6060
"asciidoctor.js": "^1.5.9",
61-
"chalk": "^2.4.2",
6261
"codelyzer": "^5.1.2",
6362
"conventional-changelog": "^2.0.3",
6463
"cz-conventional-changelog": "3.0.2",
27.8 KB
Binary file not shown.

schematics/scully/src/add-blog/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export default function(options: Schema): Rule {
88
const makrdownOptions: MarkownSchema = {
99
name: 'blog',
1010
slug: 'slug',
11+
sourceDir: 'blog',
12+
route: 'blog',
1113
};
1214

1315
if (options.routingScope) {

schematics/scully/src/add-blog/index_spec.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,41 @@ describe('add-blog schematic', () => {
2323
appTree = await schematicRunner.runSchematicAsync('blog', defaultOptions, appTree).toPromise();
2424
});
2525

26-
it('should have run the markdown schematic', () => {
26+
it('should have run the markdown schematic with default options', () => {
2727
expect(
2828
schematicRunner.tasks.some(
2929
task =>
3030
task.name === 'run-schematic' &&
3131
(task.options as any).name === 'create-markdown' &&
3232
(task.options as any).options.name === 'blog' &&
33+
(task.options as any).options.sourceDir === 'blog' &&
34+
(task.options as any).options.route === 'blog' &&
3335
(task.options as any).options.slug === 'slug'
3436
)
3537
).toBe(true);
3638
});
3739
});
40+
41+
describe('when setting an explicit `routingScope`', () => {
42+
beforeEach(async () => {
43+
appTree = await schematicRunner
44+
.runSchematicAsync('blog', {...defaultOptions, routingScope: 'Root'}, appTree)
45+
.toPromise();
46+
});
47+
48+
it('should have run the markdown schematic with defaults and `routingScope`', () => {
49+
expect(
50+
schematicRunner.tasks.some(
51+
task =>
52+
task.name === 'run-schematic' &&
53+
(task.options as any).name === 'create-markdown' &&
54+
(task.options as any).options.name === 'blog' &&
55+
(task.options as any).options.sourceDir === 'blog' &&
56+
(task.options as any).options.route === 'blog' &&
57+
(task.options as any).options.slug === 'slug' &&
58+
(task.options as any).options.routingScope === 'Root'
59+
)
60+
).toBe(true);
61+
});
62+
});
3863
});

schematics/scully/src/add-blog/schema.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "http://json-schema.org/schema",
3-
"id": "add-blog",
4-
"title": "Scully add component schematic",
3+
"id": "@scullyio/init:blog",
4+
"title": "Scully: Add a complete blog schematic",
55
"type": "object",
66
"properties": {
77
"routingScope": {

schematics/scully/src/add-post/index.ts

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import {Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics';
22
import {strings} from '@angular-devkit/core';
3-
import fs = require('fs');
4-
import yaml = require('js-yaml');
53

64
import {Schema} from './schema';
5+
import {yamlToJson, jsonToJaml} from '../utils/utils';
76

87
export default function(options: Schema): Rule {
98
return (host: Tree, context: SchematicContext) => {
@@ -19,19 +18,10 @@ export default function(options: Schema): Rule {
1918
};
2019

2120
if (options.metaDataFile) {
22-
let metaDataContents = '';
23-
try {
24-
metaDataContents = fs.readFileSync(options.metaDataFile, 'utf8');
25-
} catch (e) {
26-
throw new SchematicsException(`File ${options.metaDataFile} not found`);
27-
}
28-
29-
try {
30-
// check if yaml is valid
31-
metaData = yaml.safeLoad(metaDataContents);
21+
const metaDataAsJson = yamlToJson(options.metaDataFile);
22+
if (metaDataAsJson) {
23+
metaData = metaDataAsJson;
3224
context.logger.info(`✅️ Meta Data File ${options.metaDataFile} successfully parsed`);
33-
} catch (e) {
34-
throw new SchematicsException(`${options.metaDataFile} contains no valid yaml`);
3525
}
3626
}
3727

@@ -40,7 +30,7 @@ export default function(options: Schema): Rule {
4030

4131
if (!host.exists(filename)) {
4232
const content = `---
43-
${yaml.safeDump(metaData)}---
33+
${jsonToJaml(metaData)}---
4434
4535
# ${name}
4636
`;

schematics/scully/src/add-post/index_spec.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,21 @@ describe('add-post', () => {
7272
expect(mdFileContent).toMatch(/language: en/g);
7373
});
7474
});
75+
76+
describe('when target file already exists', () => {
77+
beforeEach(() => {
78+
appTree.create(expectedFileName, 'foo');
79+
});
80+
81+
it('should not touch existing file', async () => {
82+
let error = '';
83+
try {
84+
await schematicRunner.runSchematicAsync('post', defaultOptions, appTree).toPromise();
85+
} catch (e) {
86+
error = e;
87+
}
88+
expect(error).toMatch(/Error: foo-bar-baz exist/g);
89+
expect(getFileContent(appTree, expectedFileName)).toEqual('foo');
90+
});
91+
});
7592
});

schematics/scully/src/add-post/schema.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "http://json-schema.org/schema",
3-
"id": "Scully-ng-add-blog",
4-
"title": "Scully ng-add-blog schematic",
3+
"id": "@scullyio/init:post",
4+
"title": "Scully: Add a blog post schematic",
55
"type": "object",
66
"properties": {
77
"name": {

schematics/scully/src/collection.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
},
1111
"scully": {
1212
"description": "Add scully to your angular app.",
13-
"factory": "./scully/index#scully",
13+
"factory": "./scully/index",
1414
"schema": "./scully/schema.json",
1515
"aliases": ["run"]
1616
},
Lines changed: 64 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,82 @@
11
import {Rule, Tree, url, applyTemplates, move, chain, SchematicContext} from '@angular-devkit/schematics';
22
import {strings, normalize} from '@angular-devkit/core';
3-
import {Schema as MyServiceSchema} from './schema';
3+
import {Schema} from './schema';
4+
import {Schema as PostSchema} from '../add-post/schema';
45
import {
56
addRouteToModule,
67
addRouteToScullyConfig,
78
applyWithOverwrite,
89
getPrefix,
910
getSrc,
11+
getFileContents,
1012
} from '../utils/utils';
13+
import {RunSchematicTask} from '@angular-devkit/schematics/tasks';
1114

1215
const SCULLY_CONF_FILE = '/scully.config.js';
1316
const ANGULAR_CONF_FILE = './angular.json';
1417

15-
export default function(options: MyServiceSchema): Rule {
16-
return (host: Tree, context: SchematicContext) => {
17-
try {
18-
const sourceDir = getSrc(host);
19-
const name = options.name ? options.name : 'blog';
20-
const nameDasherized = strings.dasherize(options.name);
21-
const targetDirName = options.sourceDir
22-
? strings.dasherize(options.sourceDir) // use sourceDir when provided
23-
: strings.dasherize(options.name); // fall back to name when not provided
24-
const date = new Date();
25-
// format yyyy-mm-dd
26-
const fullDay = date.toISOString().substring(0, 10);
27-
const path = `${targetDirName}/${fullDay}-${nameDasherized}.md`;
28-
if (!host.exists(path)) {
29-
host.create(
30-
path,
31-
`---
32-
title: This is the ${name}
33-
description: ${name} description
34-
publish: false
35-
---
18+
export default (options: Schema): Rule => {
19+
options.name = options.name || 'blog';
20+
options.slug = options.slug || 'id';
21+
options.sourceDir = options.sourceDir || options.name;
22+
return chain([
23+
addPost(options, options.sourceDir),
24+
updateScullyConfig(options.sourceDir, options),
25+
addModule(options),
26+
]);
27+
};
3628

37-
# Page ${name} example
38-
`
39-
);
40-
context.logger.info(`✅ ${path} file created`);
41-
}
29+
const addPost = (options: Schema, target: string) => (tree: Tree, context: SchematicContext) => {
30+
const nameDasherized = strings.dasherize(options.name);
31+
const targetDirName = strings.dasherize(target);
32+
const date = new Date();
33+
// format yyyy-mm-dd
34+
const fullDay = date.toISOString().substring(0, 10);
35+
const baseFileName = `${fullDay}-${nameDasherized}`;
36+
if (!tree.exists(`${targetDirName}/${baseFileName}.md`)) {
37+
const postOptions: PostSchema = {
38+
name: baseFileName,
39+
target: targetDirName,
40+
};
41+
context.addTask(new RunSchematicTask('post', postOptions), []);
42+
}
43+
};
4244

43-
let scullyJs;
44-
try {
45-
scullyJs = host.read(SCULLY_CONF_FILE).toString();
46-
} catch (e) {
47-
// for test in schematics
48-
scullyJs = `exports.config = {
49-
projectRoot: "${getSrc(host)}/app",
50-
routes: {
51-
},
52-
};`;
53-
}
54-
const newScullyJs = addRouteToScullyConfig(scullyJs, {
55-
name,
56-
slug: options.slug,
57-
type: 'contentFolder',
58-
sourceDir,
59-
route: options.route,
60-
});
61-
host.overwrite(SCULLY_CONF_FILE, newScullyJs);
62-
context.logger.info(`✅️ Update ${SCULLY_CONF_FILE}`);
45+
const updateScullyConfig = (target: string, options: Schema) => (tree: Tree, context: SchematicContext) => {
46+
const scullyJs = getFileContents(tree, SCULLY_CONF_FILE);
47+
if (!scullyJs) {
48+
context.logger.error(`No scully configuration file found ${SCULLY_CONF_FILE}`);
49+
}
50+
const newScullyJs = addRouteToScullyConfig(scullyJs, {
51+
name: options.name,
52+
slug: options.slug,
53+
type: 'contentFolder',
54+
sourceDir: target,
55+
route: options.route,
56+
});
6357

64-
const pathName = strings.dasherize(`${sourceDir}/app/${name}`);
65-
let prefix = 'app';
66-
if (host.exists(ANGULAR_CONF_FILE)) {
67-
prefix = getPrefix(host.read(ANGULAR_CONF_FILE).toString());
68-
addRouteToModule(host, options);
69-
}
58+
tree.overwrite(SCULLY_CONF_FILE, newScullyJs);
59+
context.logger.info(`✅️ Update ${SCULLY_CONF_FILE}`);
60+
};
7061

71-
const templateSource = applyWithOverwrite(url('../files/markdown-module'), [
72-
applyTemplates({
73-
classify: strings.classify,
74-
dasherize: strings.dasherize,
75-
name: options.name,
76-
slug: options.slug,
77-
prefix,
78-
}),
79-
move(normalize(pathName)),
80-
]);
62+
const addModule = (options: Schema) => (tree: Tree, context: SchematicContext) => {
63+
const sourceDir = getSrc(tree);
64+
const pathName = strings.dasherize(`${sourceDir}/app/${options.name}`);
65+
let prefix = 'app';
66+
if (tree.exists(ANGULAR_CONF_FILE)) {
67+
prefix = getPrefix(getFileContents(tree, ANGULAR_CONF_FILE));
68+
addRouteToModule(tree, options);
69+
}
8170

82-
return chain([templateSource]);
83-
} catch (e) {}
84-
};
85-
}
71+
return applyWithOverwrite(url('../files/markdown-module'), [
72+
applyTemplates({
73+
classify: strings.classify,
74+
dasherize: strings.dasherize,
75+
camelize: strings.camelize,
76+
name: options.name,
77+
slug: options.slug,
78+
prefix,
79+
}),
80+
move(normalize(pathName)),
81+
]);
82+
};

0 commit comments

Comments
 (0)