Skip to content

Commit

Permalink
Enabling default app/page templates for user workspace (#52)
Browse files Browse the repository at this point in the history
* fix: updating init context var, conditions, adding additional tests

* test: removing test function, readding tests

* docs: remove TODO from readme

* test: fix merged tests
  • Loading branch information
hutchgrant authored and thescientist13 committed Apr 28, 2019
1 parent c90c662 commit afcd21e
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 70 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,6 @@ Here are some of the features and capabiliites of Greenwood.
> https://github.com/ProjectEvergreen/greenwood/issues/27
### Templates
> TODO
> https://github.com/ProjectEvergreen/greenwood/issues/32

By default, Greenwood will supply its own [app-template](https://github.com/ProjectEvergreen/greenwood/blob/master/packages/cli/templates/app-template.js) and [page-template](https://github.com/ProjectEvergreen/greenwood/blob/master/packages/cli/templates/page-template.js). You can override these files by creating a src/templates directory in your application along with both template files.

Expand Down
67 changes: 27 additions & 40 deletions packages/cli/lib/init.js
Original file line number Diff line number Diff line change
@@ -1,57 +1,44 @@
const fs = require('fs');
const path = require('path');
const greenwoodWorkspace = path.join(__dirname, '..');
const defaultTemplateDir = path.join(greenwoodWorkspace, 'templates/');
const defaultSrc = path.join(process.cwd(), 'src');
const defaultTemplatesDir = path.join(__dirname, '../templates/');
const scratchDir = path.join(process.cwd(), './.greenwood/');

const userWorkspace = fs.existsSync(defaultSrc)
? defaultSrc
: defaultTemplateDir;

const pagesDir = fs.existsSync(path.join(userWorkspace, 'pages'))
? path.join(userWorkspace, 'pages/')
: defaultTemplateDir;

const templatesDir = fs.existsSync(path.join(userWorkspace, 'templates'))
? path.join(userWorkspace, 'templates/')
: defaultTemplateDir;
const publicDir = path.join(process.cwd(), './public');

module.exports = initContexts = async() => {

return new Promise((resolve, reject) => {

try {

const context = {
userWorkspace,
pagesDir,
// TODO: replace user workspace src path based on config see issue #40
// https://github.com/ProjectEvergreen/greenwood/issues/40
const userWorkspace = path.join(process.cwd(), 'src');
const userPagesDir = path.join(userWorkspace, 'pages/');
const userTemplatesDir = path.join(userWorkspace, 'templates/');
const userAppTemplate = path.join(userTemplatesDir, 'app-template.js');
const userPageTemplate = path.join(userTemplatesDir, 'page-template.js');

const userHasWorkspace = fs.existsSync(userWorkspace);
const userHasWorkspacePages = fs.existsSync(userPagesDir);
const userHasWorkspaceTemplates = fs.existsSync(userTemplatesDir);
const userHasWorkspacePageTemplate = fs.existsSync(userPageTemplate);
const userHasWorkspaceAppTemplate = fs.existsSync(userAppTemplate);

let context = {
scratchDir,
templatesDir,
publicDir: path.join(process.cwd(), './public'),
pageTemplate: 'page-template.js',
appTemplate: 'app-template.js',
publicDir,
pagesDir: userHasWorkspacePages ? userPagesDir : defaultTemplatesDir,
templatesDir: userHasWorkspaceTemplates ? userTemplatesDir : defaultTemplatesDir,
userWorkspace: userHasWorkspace ? userWorkspace : defaultTemplatesDir,
pageTemplatePath: userHasWorkspacePageTemplate
? userPageTemplate
: path.join(defaultTemplatesDir, 'page-template.js'),
appTemplatePath: userHasWorkspaceAppTemplate
? userAppTemplate
: path.join(defaultTemplatesDir, 'app-template.js'),
indexPageTemplate: 'index.html',
notFoundPageTemplate: '404.html'
};

// TODO allow per template overrides
if (fs.existsSync(context.templatesDir)) {

// https://github.com/ProjectEvergreen/greenwood/issues/30
if (!fs.existsSync(path.join(context.templatesDir, context.pageTemplate))) {
reject('It looks like you don\'t have a page template defined. \n' +
'Please include a page-template.js in your templates directory. \n' +
'See https://github.com/ProjectEvergreen/greenwood/blob/master/packages/cli/templates/page-template.js');
}

// https://github.com/ProjectEvergreen/greenwood/issues/32
if (!fs.existsSync(path.join(context.templatesDir, context.appTemplate))) {
reject('It looks like you don\'t have an app template defined. \n' +
'Please include an app-template.js in your templates directory. \n' +
'See https://github.com/ProjectEvergreen/greenwood/blob/master/packages/cli/templates/app-template.js');
}
}
if (!fs.existsSync(scratchDir)) {
fs.mkdirSync(scratchDir);
}
Expand Down
15 changes: 10 additions & 5 deletions packages/cli/lib/scaffold.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ const fs = require('fs');
const path = require('path');

const writePageComponentsFromTemplate = async (compilation) => {
const createPageComponent = async (file) => {
const createPageComponent = async (file, context) => {
return new Promise(async (resolve, reject) => {
try {
let data = await fs.readFileSync(path.join(compilation.context.templatesDir, `${file.template}-template.js`));
let result = data.toString().replace(/entry/g, `wc-md-${file.label}`);
const pageTemplatePath = file.template === 'page'
? context.pageTemplatePath
: path.join(context.templatesDir, `${file.template}-template.js`);

const templateData = await fs.readFileSync(pageTemplatePath);

let result = templateData.toString().replace(/entry/g, `wc-md-${file.label}`);

result = result.replace(/page-template/g, `eve-${file.label}`);
result = result.replace(/MDIMPORT;/, `import '${file.mdFile}';`);
Expand All @@ -23,7 +28,7 @@ const writePageComponentsFromTemplate = async (compilation) => {

return new Promise(async(resolve, reject) => {
try {
let result = await createPageComponent(file);
let result = await createPageComponent(file, context);

let relPageDir = file.filePath.substring(context.pagesDir.length, file.filePath.length);
const pathLastBackslash = relPageDir.lastIndexOf('/');
Expand Down Expand Up @@ -66,7 +71,7 @@ const writeListImportFile = async (compilation) => {
const writeRoutes = async(compilation) => {
return new Promise(async (resolve, reject) => {
try {
let data = await fs.readFileSync(path.join(compilation.context.templatesDir, `${compilation.context.appTemplate}`));
let data = await fs.readFileSync(compilation.context.appTemplatePath);

const routes = compilation.graph.map(file => {
return `<lit-route path="${file.route}" component="eve-${file.label}"></lit-route>\n\t\t\t\t`;
Expand Down
192 changes: 169 additions & 23 deletions test/cli.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const glob = require('glob-promise');
const TestSetup = require('./setup');
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
let CONTEXT;

describe('building greenwood with default context (no user workspace)', () => {

Expand Down Expand Up @@ -93,7 +94,7 @@ describe('building greenwood with default context (no user workspace)', () => {
});

describe('building greenwood with a user workspace w/custom nested pages directories', () => {

before(async() => {
setup = new TestSetup();
CONTEXT = await setup.init();
Expand All @@ -104,7 +105,7 @@ describe('building greenwood with a user workspace w/custom nested pages directo
blogPageHtmlPath = path.join(CONTEXT.publicDir, 'blog', '20190326', 'index.html');
customFMPageHtmlPath = path.join(CONTEXT.publicDir, 'customfm', 'index.html');
});

it('should output one JS bundle', async() => {
expect(await glob.promise(path.join(CONTEXT.publicDir, './**/index.*.bundle.js'))).to.have.lengthOf(1);
});
Expand Down Expand Up @@ -153,13 +154,13 @@ describe('building greenwood with a user workspace w/custom nested pages directo
});

it('should have the expected heading text within the customfm page in the customfm directory', async() => {
const heading = dom.window.document.querySelector('h3').textContent;
const heading = dom.window.document.querySelector('h3.wc-md-customfm').textContent;

expect(heading).to.equal(defaultPageHeading);
});

it('should have the expected paragraph text within the customfm page in the customfm directory', async() => {
let paragraph = dom.window.document.querySelector('p').textContent;
let paragraph = dom.window.document.querySelector('p.wc-md-customfm').textContent;

expect(paragraph).to.equal(defaultPageBody);
});
Expand All @@ -176,42 +177,187 @@ describe('building greenwood with a user workspace w/custom nested pages directo
await fs.remove(CONTEXT.publicDir);
await fs.remove(CONTEXT.scratchDir);
});

});

// TODO - https://github.com/ProjectEvergreen/greenwood/issues/32
// describe('building greenwood with a user workspace w/custom app-template override', () => {
describe('building greenwood with user workspace that doesn\'t contain app template', () => {
before(async() => {
setup = new TestSetup();
CONTEXT = await setup.init();
// copy test app
await fs.copy(CONTEXT.testApp, CONTEXT.userSrc);
await setup.run(['./packages/cli/index.js', 'build']);
await fs.removeSync(path.join(CONTEXT.userSrc, 'templates', 'app-template.js'));

blogPageHtmlPath = path.join(CONTEXT.publicDir, 'blog', '20190326', 'index.html');
});

it('should create a public directory', () => {
expect(fs.existsSync(CONTEXT.publicDir)).to.be.true;
});

describe('public directory output', () => {
it('should output a single index.html file (home page)', () => {
expect(fs.existsSync(path.join(CONTEXT.publicDir, './index.html'))).to.be.true;
});

// });
it('should output one JS bundle', async() => {
expect(await glob.promise(path.join(CONTEXT.publicDir, './**/index.*.bundle.js'))).to.have.lengthOf(1);
});

describe('building greenwood with error handling for app and page templates', () => {
before(async () => {
it('should create a default hello page directory', () => {
expect(fs.existsSync(path.join(CONTEXT.publicDir, './hello'))).to.be.true;
});

describe('default generated hello page directory', () => {
const defaultHeading = 'Test App';
const defaultBody = 'This is a test app using a custom user template!';
let dom;

beforeEach(async() => {
dom = await JSDOM.fromFile(path.resolve(CONTEXT.publicDir, 'hello/index.html'));
});

it('should output an index.html file within the default hello page directory', () => {
expect(fs.existsSync(path.join(CONTEXT.publicDir, './hello', './index.html'))).to.be.true;
});

it('should have the expected heading text within the hello example page in the hello directory', async() => {
const heading = dom.window.document.querySelector('h3').textContent;

expect(heading).to.equal(defaultHeading);
});

it('should have the expected paragraph text within the hello example page in the hello directory', async() => {
let paragraph = dom.window.document.querySelector('p').textContent;

expect(paragraph).to.equal(defaultBody);
});
});
});

it('should contain a nested blog page directory', () => {
expect(fs.existsSync(path.join(CONTEXT.publicDir, 'blog', '20190326'))).to.be.true;
});

describe('nested generated blog page directory', () => {
const defaultHeading = 'Blog Page';
const defaultBody = 'This is the blog page built by Greenwood.';
let dom;

beforeEach(async() => {
dom = await JSDOM.fromFile(blogPageHtmlPath);
});

it('should contain a nested blog page with an index html file', () => {
expect(fs.existsSync(blogPageHtmlPath)).to.be.true;
});

it('should have the expected heading text within the blog page in the blog directory', async() => {
const heading = dom.window.document.querySelector('h3').textContent;

expect(heading).to.equal(defaultHeading);
});

it('should have the expected paragraph text within the blog page in the blog directory', async() => {
let paragraph = dom.window.document.querySelector('p').textContent;

expect(paragraph).to.equal(defaultBody);
});
});
after(async() => {
await fs.remove(CONTEXT.userSrc);
await fs.remove(CONTEXT.publicDir);
await fs.remove(CONTEXT.scratchDir);
});
});
describe('building greenwood with user workspace that doesn\'t contain page template', () => {
before(async() => {
setup = new TestSetup();
CONTEXT = await setup.init();
// copy test app
await fs.copy(CONTEXT.testApp, CONTEXT.userSrc);
await setup.run(['./packages/cli/index.js', 'build']);
await fs.removeSync(path.join(CONTEXT.userSrc, 'templates', 'page-template.js'));

blogPageHtmlPath = path.join(CONTEXT.publicDir, 'blog', '20190326', 'index.html');
});

// create empty template directory
await fs.mkdirSync(CONTEXT.userSrc);
await fs.mkdirSync(CONTEXT.userTemplates);
it('should create a public directory', () => {
expect(fs.existsSync(CONTEXT.publicDir)).to.be.true;
});

it('should display an error if page-template.js is missing', async() => {
await setup.run(['./packages/cli/index.js'], '').catch((err) => {
expect(err).to.contain("It looks like you don't have a page template defined. ");
describe('public directory output', () => {
it('should output a single index.html file (home page)', () => {
expect(fs.existsSync(path.join(CONTEXT.publicDir, './index.html'))).to.be.true;
});
});

it('should display an error if app-template.js is missing', async () => {
// add blank page-template
await fs.writeFileSync(path.join(CONTEXT.userTemplates, 'page-template.js'), '');
await setup.run(['./packages/cli/index.js'], '').catch((err) => {
expect(err).to.contain("It looks like you don't have an app template defined. ");
it('should output one JS bundle', async() => {
expect(await glob.promise(path.join(CONTEXT.publicDir, './**/index.*.bundle.js'))).to.have.lengthOf(1);
});

it('should create a default hello page directory', () => {
expect(fs.existsSync(path.join(CONTEXT.publicDir, './hello'))).to.be.true;
});

describe('default generated hello page directory', () => {
const defaultHeading = 'Test App';
const defaultBody = 'This is a test app using a custom user template!';
let dom;

beforeEach(async() => {
dom = await JSDOM.fromFile(path.resolve(CONTEXT.publicDir, 'hello/index.html'));
});

it('should output an index.html file within the default hello page directory', () => {
expect(fs.existsSync(path.join(CONTEXT.publicDir, './hello', './index.html'))).to.be.true;
});

it('should have the expected heading text within the hello example page in the hello directory', async() => {
const heading = dom.window.document.querySelector('h3').textContent;

expect(heading).to.equal(defaultHeading);
});

it('should have the expected paragraph text within the hello example page in the hello directory', async() => {
let paragraph = dom.window.document.querySelector('p').textContent;

expect(paragraph).to.equal(defaultBody);
});
});
});

it('should contain a nested blog page directory', () => {
expect(fs.existsSync(path.join(CONTEXT.publicDir, 'blog', '20190326'))).to.be.true;
});

describe('nested generated blog page directory', () => {
const defaultHeading = 'Blog Page';
const defaultBody = 'This is the blog page built by Greenwood.';
let dom;

beforeEach(async() => {
dom = await JSDOM.fromFile(blogPageHtmlPath);
});

it('should contain a nested blog page with an index html file', () => {
expect(fs.existsSync(blogPageHtmlPath)).to.be.true;
});

it('should have the expected heading text within the blog page in the blog directory', async() => {
const heading = dom.window.document.querySelector('h3').textContent;

expect(heading).to.equal(defaultHeading);
});

it('should have the expected paragraph text within the blog page in the blog directory', async() => {
let paragraph = dom.window.document.querySelector('p').textContent;

expect(paragraph).to.equal(defaultBody);
});
});
after(async() => {
await fs.remove(CONTEXT.userSrc);
await fs.remove(CONTEXT.publicDir);
await fs.remove(CONTEXT.scratchDir);
});

});

0 comments on commit afcd21e

Please sign in to comment.