Skip to content

Commit

Permalink
Implementing generator for single-spa root configs. (single-spa#9)
Browse files Browse the repository at this point in the history
* Adding prompts and options for generating root configs and raw in-browser modules

* Self review

* Self review

* Implementing generator for single-spa root configs.

* Justin's feedback

* Making org name templatized

* More fixes

* Fixing build

* Fixing build

* Self review
  • Loading branch information
joeldenning authored Dec 19, 2019
1 parent ec5babc commit b7ab626
Show file tree
Hide file tree
Showing 15 changed files with 272 additions and 14 deletions.
7 changes: 7 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
language: node_js
node_js:
- "node"
script:
- yarn bootstrap
- yarn test
- yarn lint
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
"name": "root",
"private": true,
"devDependencies": {
"lerna": "^3.15.0",
"yeoman-assert": "^3.1.1"
"lerna": "^3.15.0"
},
"scripts": {
"bootstrap": "lerna bootstrap"
"bootstrap": "lerna bootstrap",
"test": "lerna run test",
"lint": "lerna run lint"
}
}
3 changes: 2 additions & 1 deletion packages/generator-single-spa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"src"
],
"scripts": {
"test": "jest"
"test": "jest --testPathIgnorePatterns templates"
},
"version": "1.0.0-alpha.0",
"main": "src/generator-single-spa.js",
Expand All @@ -19,6 +19,7 @@
"devDependencies": {
"chalk": "^3.0.0",
"jest": "^24.9.0",
"yeoman-assert": "^3.1.1",
"yeoman-test": "^2.0.0"
}
}
16 changes: 11 additions & 5 deletions packages/generator-single-spa/src/generator-single-spa.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const Generator = require('yeoman-generator')
const SingleSpaReactGenerator = require('./react/generator-single-spa-react')
const SingleSpaRootConfigGenerator = require('./root-config/generator-root-config')

module.exports = class SingleSpaGenerator extends Generator {
constructor(args, opts) {
Expand All @@ -16,8 +17,12 @@ module.exports = class SingleSpaGenerator extends Generator {
async composeChildGenerator() {
let moduleType = this.options.moduleType

if (!moduleType && this.options.framework) {
moduleType = 'app-parcel'
}

if (!moduleType) {
const answers = await this.prompt([
moduleType = (await this.prompt([
{
type: 'list',
name: 'moduleType',
Expand All @@ -28,13 +33,14 @@ module.exports = class SingleSpaGenerator extends Generator {
{ name: 'single-spa root config', value: 'root-config' },
]
}
])

moduleType = answers.moduleType
])).moduleType
}

if (moduleType === 'root-config') {
throw Error('root config not yet implemented')
this.composeWith({
Generator: SingleSpaRootConfigGenerator,
path: require.resolve('./root-config/generator-root-config.js'),
}, this.options)
} else if (moduleType === 'app-parcel') {
await runFrameworkGenerator.call(this)
} else if (moduleType === 'raw-module') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
const Generator = require('yeoman-generator')
const fs = require('fs').promises
const chalk = require('chalk')

let times = 0

module.exports = class SingleSpaRootConfigGenerator extends Generator {
constructor(args, opts) {
super(args, opts)

this.option("packageManager", {
type: String
})
}
async createPackageJson() {
this.packageManager = this.options.packageManager

if (!this.packageManager) {
this.packageManager = (await this.prompt([
{
type: "list",
name: "packageManager",
message: "Which package manager do you want to use?",
choices: [
"yarn",
"npm",
]
}
])).packageManager
}

const templatePackageJson = JSON.parse(await fs.readFile(this.templatePath('package.json')))

this.fs.extendJSON(
this.destinationPath("package.json"),
templatePackageJson
)
}
async copyFiles() {
const templateOptions = await this.prompt([
{
type: "input",
name: "orgName",
message: "Organization name (use lowercase and dashes)",
},
])

this.fs.copyTpl(
this.templatePath(),
this.destinationPath(),
templateOptions,
{delimiter: '?'}
)
}
install() {
this.installDependencies({
npm: this.packageManager === 'npm',
yarn: this.packageManager === 'yarn',
bower: false,
})
}
finished() {
this.on(`${this.packageManager}Install:end`, () => {
const coloredFinalInstructions = chalk.bgWhite.black
console.log(coloredFinalInstructions("Project setup complete!"))
console.log(coloredFinalInstructions(`Run '${this.packageManager} start' to boot up your single-spa root config`))
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": [
"important-stuff",
"plugin:prettier/recommended"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"scripts": {
"start": "webpack-dev-server --mode=development --port 9000 --env.local=true",
"lint": "eslint src",
"test": "jest",
"build": "webpack --mode=production"
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged && eslint src"
}
},
"devDependencies": {
"@babel/core": "^7.7.4",
"@babel/preset-env": "^7.7.4",
"@types/systemjs": "^6.1.0",
"babel-loader": "^8.0.6",
"clean-webpack-plugin": "^3.0.0",
"eslint": "^6.7.2",
"eslint-config-important-stuff": "^1.1.0",
"eslint-config-prettier": "^6.7.0",
"eslint-plugin-prettier": "^3.1.1",
"html-webpack-plugin": "^3.2.0",
"husky": "^3.1.0",
"jest": "^24.9.0",
"jest-cli": "^24.9.0",
"prettier": "^1.19.1",
"pretty-quick": "^2.0.1",
"serve": "^11.2.0",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0"
},
"dependencies": {
"single-spa": "^4.4.2"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export function navbar(location) {
// The navbar is always active
return true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as isActive from './activity-functions'

describe('activity functions', () => {
it('verifies that the navbar is always active', () => {
expect(isActive.navbar(location)).toBe(true)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Root Config</title>
<meta name="importmap-type" content="systemjs-importmap" />
<script type="systemjs-importmap" src="https://storage.googleapis.com/react.microfrontends.app/importmap.json"></script>
<% if (isLocal) { %>
<script type="systemjs-importmap">
{
"imports": {
"@<?- orgName ?>/root-config": "http://localhost:9000/root-config.js"
}
}
</script>
<% } %>
<script src="https://cdn.jsdelivr.net/npm/import-map-overrides/dist/import-map-overrides.js"></script>
<script src="https://cdn.jsdelivr.net/npm/systemjs/dist/system.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/systemjs/dist/extras/amd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/systemjs/dist/extras/named-exports.min.js"></script>
</head>
<body>
<script>
System.import('@<?- orgName ?>/root-config');
</script>
<import-map-overrides-full show-when-local-storage="devtools" dev-libs></import-map-overrides-full>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { registerApplication, start } from "single-spa";
import * as isActive from "./activity-functions";

registerApplication(
"@<?- orgName ?>/navbar",
() => System.import("@<?- orgName ?>/navbar"),
isActive.navbar
);

start();
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = env => ({
entry: path.resolve(__dirname, "src/root-config"),
output: {
filename: "root-config.js",
libraryTarget: "system",
path: path.resolve(__dirname, "dist")
},
devtool: "sourcemap",
module: {
rules: [
{ parser: { system: false } },
{
test: /\.js$/,
exclude: /node_modules/,
use: [{ loader: "babel-loader" }]
}
]
},
devServer: {
historyApiFallback: true
},
plugins: [
new HtmlWebpackPlugin({
inject: false,
template: "src/index.ejs",
templateParameters: {
isLocal: env && env.isLocal
}
}),
new CleanWebpackPlugin()
],
externals: ["single-spa", /^@<?- orgName ?>\/.+$/]
});
43 changes: 43 additions & 0 deletions packages/generator-single-spa/test/root-config.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const path = require('path')
const fs = require('fs')
const generator = require('../src/generator-single-spa')
const helpers = require('yeoman-test')
const assert = require('yeoman-assert')

describe('generator-single-spa', () => {
let runContext

afterEach(() => {
runContext.cleanTestDirectory()
})

it('can run the generator', () => {
runContext = helpers.run(generator)
.withOptions({
moduleType: "root-config",
})
.withPrompts({
packageManager: "yarn",
orgName: 'some-org-name'
})

return runContext.then(dir => {
assert.file(path.join(dir, 'package.json'))

// The webpack config should have their org name in its webpack externals
assert.file(path.join(dir, "webpack.config.js"))
const webpackConfigAsStr = fs.readFileSync(path.join(dir, 'webpack.config.js'), {encoding: "utf-8"})
expect(webpackConfigAsStr.includes('some-org-name')).toBe(true)

// The index.ejs file should have their org name in it
assert.file(path.join(dir, "src/index.ejs"))
const indexEjsStr = fs.readFileSync(path.join(dir, 'src/index.ejs'), {encoding: 'utf-8'})
expect(indexEjsStr.includes('some-org-name')).toBe(true)

// The root-config.js file should have their org name in it
assert.file(path.join(dir, "src/root-config.js"))
const rootConfigStr = fs.readFileSync(path.join(dir, 'src/root-config.js'), {encoding: 'utf-8'})
expect(rootConfigStr.includes('some-org-name')).toBe(true)
})
})
})
5 changes: 5 additions & 0 deletions packages/generator-single-spa/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4432,6 +4432,11 @@ yargs@^13.3.0:
y18n "^4.0.0"
yargs-parser "^13.1.1"

yeoman-assert@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yeoman-assert/-/yeoman-assert-3.1.1.tgz#9f6fa0ecba7dd007c40f579668cb5dda18c79343"
integrity sha512-bCuLb/j/WzpvrJZCTdJJLFzm7KK8IYQJ3+dF9dYtNs2CUYyezFJDuULiZ2neM4eqjf45GN1KH/MzCTT3i90wUQ==

yeoman-environment@^2.0.5, yeoman-environment@^2.3.0, yeoman-environment@^2.3.4:
version "2.6.0"
resolved "https://registry.yarnpkg.com/yeoman-environment/-/yeoman-environment-2.6.0.tgz#db884c778946fec9a41e8980a6b3aa94fe41302d"
Expand Down
5 changes: 0 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4830,8 +4830,3 @@ yargs@^14.2.2:
which-module "^2.0.0"
y18n "^4.0.0"
yargs-parser "^15.0.0"

yeoman-assert@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yeoman-assert/-/yeoman-assert-3.1.1.tgz#9f6fa0ecba7dd007c40f579668cb5dda18c79343"
integrity sha512-bCuLb/j/WzpvrJZCTdJJLFzm7KK8IYQJ3+dF9dYtNs2CUYyezFJDuULiZ2neM4eqjf45GN1KH/MzCTT3i90wUQ==

0 comments on commit b7ab626

Please sign in to comment.