├── .nvmrc
├── examples
└── design
│ ├── css
│ └── webflow.css
│ ├── js
│ └── webflow.js
│ ├── fonts
│ └── Avenir-Book.ttf
│ ├── images
│ └── webflow.jpg
│ └── index.html
├── .eslintrc
├── test
├── mocha.opts
└── integration
│ ├── create.js
│ ├── generate.js
│ └── update.js
├── .eslintignore
├── lib
├── create
│ ├── templates
│ │ ├── .gitignore
│ │ ├── .eslintrc
│ │ ├── .eslintignore
│ │ ├── client
│ │ │ ├── imports
│ │ │ │ ├── custom
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── components
│ │ │ │ │ │ └── App.jsx
│ │ │ │ └── generated
│ │ │ │ │ └── components
│ │ │ │ │ └── App.jsx
│ │ │ ├── main.html
│ │ │ └── main.jsx
│ │ ├── .travis.yml
│ │ └── tests
│ │ │ └── app-should-exist.js
│ ├── copy-boilerplate.js
│ ├── log-instruction.js
│ ├── create-meteor.js
│ ├── install-meteor-dependencies.js
│ ├── index.js
│ ├── add-script-commands.js
│ └── install-npm-dependencies.js
├── plugins
│ ├── apollo
│ │ ├── templates
│ │ │ ├── client
│ │ │ │ └── index.js
│ │ │ ├── server
│ │ │ │ └── index.js
│ │ │ └── imports
│ │ │ │ ├── apollo
│ │ │ │ ├── mocks.js
│ │ │ │ ├── schema.js
│ │ │ │ ├── connectors.js
│ │ │ │ └── resolvers.js
│ │ │ │ └── startup
│ │ │ │ ├── client
│ │ │ │ └── index.js
│ │ │ │ └── server
│ │ │ │ └── index.js
│ │ ├── index.js
│ │ └── after-create.js
│ └── index.js
├── helpers
│ ├── shelljs.js
│ ├── log-task.js
│ ├── read-one-html.js
│ └── regenerate-folder-in-public.js
├── update
│ ├── copy-images.js
│ ├── index.js
│ ├── run-reacterminator.js
│ ├── unzip-design.js
│ ├── check-dot-design-dir.js
│ ├── copy-css.js
│ └── copy-fonts.js
└── generate.js
├── .gitignore
├── bin
├── stanza-update.js
├── stanza-generate.js
├── stanza-create.js
└── stanza.js
├── .travis.yml
├── package.json
└── README.md
/.nvmrc:
--------------------------------------------------------------------------------
1 | 6.1.0
2 |
--------------------------------------------------------------------------------
/examples/design/css/webflow.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/design/js/webflow.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/design/fonts/Avenir-Book.ttf:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/design/images/webflow.jpg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "poetic"
3 | }
4 |
--------------------------------------------------------------------------------
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --recursive
2 | --timeout 600000
3 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | coverage
2 | templates
3 | test/example
4 |
--------------------------------------------------------------------------------
/lib/create/templates/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .design/
3 |
--------------------------------------------------------------------------------
/lib/create/templates/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "poetic"
3 | }
4 |
--------------------------------------------------------------------------------
/lib/plugins/apollo/templates/client/index.js:
--------------------------------------------------------------------------------
1 | import '../imports/startup/client/index';
2 |
--------------------------------------------------------------------------------
/lib/plugins/apollo/templates/server/index.js:
--------------------------------------------------------------------------------
1 | import '../imports/startup/server/index';
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | npm-debug.log
4 | doc
5 | examples/example
6 | .coveralls.yml
7 |
--------------------------------------------------------------------------------
/lib/create/templates/.eslintignore:
--------------------------------------------------------------------------------
1 | .design/
2 | node_modules/
3 | client/js/
4 | client/imports/generated/
5 |
--------------------------------------------------------------------------------
/lib/helpers/shelljs.js:
--------------------------------------------------------------------------------
1 | const shell = require('shelljs');
2 |
3 | shell.config.verbose = true;
4 |
5 | module.exports = shell;
6 |
--------------------------------------------------------------------------------
/lib/plugins/apollo/index.js:
--------------------------------------------------------------------------------
1 | const afterCreate = require('./after-create');
2 |
3 | module.exports = {
4 | afterCreate,
5 | };
6 |
--------------------------------------------------------------------------------
/lib/plugins/apollo/templates/imports/apollo/mocks.js:
--------------------------------------------------------------------------------
1 | const mocks = {
2 | String: () => "It works!",
3 | };
4 |
5 | export default mocks;
6 |
--------------------------------------------------------------------------------
/lib/create/templates/client/imports/custom/index.js:
--------------------------------------------------------------------------------
1 | import ComponentsApp from './components/App';
2 |
3 | export default {
4 | 'components/App': ComponentsApp,
5 | };
6 |
--------------------------------------------------------------------------------
/lib/helpers/log-task.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 |
3 | const chalk = require('chalk');
4 |
5 | module.exports = function logTask(task) {
6 | console.log(chalk.green.bold(`===== ${task}`));
7 | };
8 |
--------------------------------------------------------------------------------
/lib/update/copy-images.js:
--------------------------------------------------------------------------------
1 | const regenerateFolderInPublic = require('../helpers/regenerate-folder-in-public');
2 |
3 | module.exports = function copyImages() {
4 | regenerateFolderInPublic('images');
5 | };
6 |
--------------------------------------------------------------------------------
/lib/plugins/apollo/templates/imports/apollo/schema.js:
--------------------------------------------------------------------------------
1 | const typeDefinitions = `
2 | type Query {
3 | testString: String
4 | }
5 |
6 | schema {
7 | query: Query
8 | }
9 | `;
10 |
11 | export default [typeDefinitions];
12 |
--------------------------------------------------------------------------------
/lib/create/templates/client/main.html:
--------------------------------------------------------------------------------
1 |
2 | App
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/lib/helpers/read-one-html.js:
--------------------------------------------------------------------------------
1 | const glob = require('glob');
2 | const fs = require('fs');
3 |
4 | module.exports = function readOneHtml() {
5 | const firstHtmlFilePath = glob.sync('.design/*.html')[0];
6 | return fs.readFileSync(firstHtmlFilePath, 'utf-8');
7 | };
8 |
--------------------------------------------------------------------------------
/lib/plugins/apollo/templates/imports/startup/client/index.js:
--------------------------------------------------------------------------------
1 | import ApolloClient from 'apollo-client';
2 | import { meteorClientConfig } from 'meteor/apollo';
3 | import { Meteor } from 'meteor/meteor';
4 |
5 | Meteor.startup(() => {
6 | const client = new ApolloClient(meteorClientConfig());
7 | });
8 |
--------------------------------------------------------------------------------
/lib/create/templates/client/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Meteor } from 'meteor/meteor';
3 | import { render } from 'react-dom';
4 | import App from './imports/generated/components/App.jsx';
5 |
6 | Meteor.startup(() => {
7 | render(, document.getElementById('render-target'));
8 | });
9 |
--------------------------------------------------------------------------------
/lib/create/templates/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - "5"
5 | before_install:
6 | - curl https://install.meteor.com | /bin/sh
7 | - export PATH="$HOME/.meteor:$PATH"
8 | - npm install -g chimp
9 | before_script:
10 | - nohup bash -c "meteor 2>&1 &" && sleep 30; cat nohup.out
11 | services:
12 | - mongodb
13 |
--------------------------------------------------------------------------------
/bin/stanza-update.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 | /* eslint-disable no-console */
3 |
4 | const program = require('commander');
5 | const update = require('../lib/update/index');
6 |
7 | program.on('--help', () => {
8 | console.log('Update the meteor project using the design.zip from webflow');
9 | });
10 |
11 | program.parse(process.argv);
12 |
13 | update();
14 |
--------------------------------------------------------------------------------
/lib/create/copy-boilerplate.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const { cp } = require('../helpers/shelljs');
3 | const logTask = require('../helpers/log-task');
4 |
5 | module.exports = function copyBoilerplate() {
6 | // copy boilerplate
7 | logTask('Copy boilerplate');
8 | const templatesPath = path.resolve(__dirname, './templates');
9 | cp('-R', `${templatesPath}/.`, './');
10 | };
11 |
--------------------------------------------------------------------------------
/lib/helpers/regenerate-folder-in-public.js:
--------------------------------------------------------------------------------
1 | const logTask = require('../helpers/log-task');
2 | const { mkdir, rm, cp } = require('../helpers/shelljs');
3 |
4 | module.exports = function regenerateFolderInPublic(folder) {
5 | logTask(`Regenerate ${folder}`);
6 | rm('-rf', `public/${folder}`);
7 | mkdir('-p', `public/${folder}`);
8 | cp(`.design/${folder}/*`, `public/${folder}`);
9 | };
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | cache:
4 | directories:
5 | - node_modules
6 | notifications:
7 | email: false
8 | before_install:
9 | - curl https://install.meteor.com | /bin/sh
10 | - export PATH="$HOME/.meteor:$PATH"
11 | before_script:
12 | - npm prune
13 | after_success:
14 | - npm run semantic-release
15 | branches:
16 | except:
17 | - /^v\d+\.\d+\.\d+$/
18 |
--------------------------------------------------------------------------------
/lib/plugins/apollo/templates/imports/apollo/connectors.js:
--------------------------------------------------------------------------------
1 | class SampleConnector {
2 | constructor(){
3 | this.store = new Map()
4 | }
5 |
6 | get(key){
7 | return this.store.get(key);
8 | }
9 |
10 | set(key, value){
11 | return this.store.set(key, value);
12 | }
13 | }
14 |
15 | const connectors = {
16 | sample: SampleConnector
17 | };
18 |
19 | export default connectors;
20 |
--------------------------------------------------------------------------------
/bin/stanza-generate.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 | /* eslint-disable no-console */
3 |
4 | const program = require('commander');
5 | const generate = require('../lib/generate');
6 |
7 | program
8 | .arguments('')
9 | .action(generate);
10 |
11 | program.on('--help', () => {
12 | console.log('Create a meteor project with react and redux configured.');
13 | });
14 |
15 | program.parse(process.argv);
16 |
--------------------------------------------------------------------------------
/lib/create/log-instruction.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | const chalk = require('chalk');
3 |
4 | module.exports = function logInstruction(projectName) {
5 | console.log('\n==============================================\n');
6 | console.log(chalk.red('TODO:'));
7 | console.log(chalk.green(`
8 | 1. cd ${projectName}
9 | 2. copy webflow zip file here as design.zip
10 | 3. stanza update
11 | `));
12 | };
13 |
--------------------------------------------------------------------------------
/lib/create/templates/client/imports/generated/components/App.jsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | import React from 'react'
3 | import custom from '../../custom/index';
4 |
5 | export default class App extends React.Component {
6 | render () {
7 | return (
8 | FIND ME AT client/imports/generated/components/App.jsx
9 | )
10 | }
11 | }
12 |
13 | const customize = custom['components/App'] || ((x) =>x);
14 |
15 | export default customize(App);
16 |
--------------------------------------------------------------------------------
/lib/plugins/apollo/templates/imports/startup/server/index.js:
--------------------------------------------------------------------------------
1 | import { createApolloServer } from 'meteor/apollo';
2 | import schema from '../../apollo/schema';
3 | import mocks from '../../apollo/mocks';
4 | import resolvers from '../../apollo/resolvers';
5 | import { Meteor } from 'meteor/meteor';
6 |
7 | Meteor.startup(() => {
8 | createApolloServer({
9 | graphiql: true,
10 | pretty: true,
11 | schema,
12 | resolvers,
13 | mocks,
14 | });
15 | });
16 |
17 |
--------------------------------------------------------------------------------
/lib/plugins/apollo/templates/imports/apollo/resolvers.js:
--------------------------------------------------------------------------------
1 | /* Your connectors are defined in connector.js and used here to retrieve your
2 | * data */
3 | import Connectors from './connectors';
4 |
5 | const resolveFunctions = {
6 | /*RootQuery: {
7 | Add RootQuery code here this must be defined in your schema
8 | },
9 | RootMutation: {
10 | Add RootMutation code here this must be defined in your schema
11 | },
12 | */
13 | }
14 |
15 | export default resolveFunctions;
16 |
--------------------------------------------------------------------------------
/lib/create/create-meteor.js:
--------------------------------------------------------------------------------
1 | const { exec, mkdir, rm, cd } = require('../helpers/shelljs');
2 | const logTask = require('../helpers/log-task');
3 |
4 | module.exports = function createMeteor(projectName) {
5 | // create meteor project
6 | logTask('Create meteor project');
7 | exec(`meteor create ${projectName}`);
8 | cd(projectName);
9 |
10 | logTask('Clean client and server');
11 | rm('-rf', './client', './server');
12 | mkdir('./client', './server');
13 |
14 | logTask('Install dependencies');
15 | };
16 |
--------------------------------------------------------------------------------
/lib/update/index.js:
--------------------------------------------------------------------------------
1 | const unzipDesign = require('./unzip-design');
2 | const copyImages = require('./copy-images');
3 | const copyCss = require('./copy-css');
4 | const copyFonts = require('./copy-fonts');
5 | const runReacterminator = require('./run-reacterminator');
6 | const checkDotDesignDir = require('./check-dot-design-dir');
7 |
8 | module.exports = function update() {
9 | unzipDesign();
10 |
11 | checkDotDesignDir();
12 |
13 | copyImages();
14 |
15 | copyCss();
16 |
17 | copyFonts();
18 |
19 | runReacterminator();
20 | };
21 |
--------------------------------------------------------------------------------
/bin/stanza-create.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 | /* eslint-disable no-console */
3 |
4 | const program = require('commander');
5 | const create = require('../lib/create/index');
6 |
7 | program
8 | .arguments('')
9 | .option('--apollo, -a')
10 | .action((name, options) => {
11 | if (!name) {
12 | console.error('ERROR: No name given!');
13 | process.exit(1);
14 | }
15 |
16 | create(name, options);
17 | });
18 |
19 | program.on('--help', () => {
20 | process.exit(0);
21 | });
22 |
23 | program.parse(process.argv);
24 |
--------------------------------------------------------------------------------
/lib/update/run-reacterminator.js:
--------------------------------------------------------------------------------
1 | const logTask = require('../helpers/log-task');
2 | const reacterminator = require('reacterminator');
3 |
4 | module.exports = function runReacterminator() {
5 | logTask('Regenerate components via reacterminator');
6 | reacterminator(
7 | { type: 'path', content: '.design/' },
8 | {
9 | outputPath: 'client/imports',
10 | changeLinksForParamStore: true,
11 | generateFiles: true,
12 | recursive: true,
13 | overrideFiles: true,
14 | fileToComponent: true,
15 | }
16 | );
17 | };
18 |
--------------------------------------------------------------------------------
/lib/create/templates/tests/app-should-exist.js:
--------------------------------------------------------------------------------
1 | /* eslint-env mocha */
2 | /* eslint-disable func-names, prefer-arrow-callback */
3 |
4 | // These are Chimp globals
5 | /* globals browser assert server */
6 |
7 | // PLEASE READ: http://guide.meteor.com/testing.html#acceptance-testing
8 | describe('app should exist', function () {
9 | beforeEach(function () {
10 | browser.url('http://localhost:3000');
11 | });
12 |
13 | it('can create a list @watch', function () {
14 | const doesExist = browser.waitForExist('render-target');
15 |
16 | assert.equal(doesExist, true);
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/test/integration/create.js:
--------------------------------------------------------------------------------
1 | /* eslint-env mocha */
2 | const fs = require('fs');
3 | const { assert } = require('chai');
4 | const path = require('path');
5 | const create = require('../../lib/create');
6 | const { cd, rm } = require('shelljs');
7 |
8 | describe('create', () => {
9 | it('create a meteor app', () => {
10 | const examplesPath = path.resolve(__dirname, '../../examples');
11 | cd(examplesPath);
12 | rm('-rf', 'example');
13 |
14 | create('example');
15 |
16 | assert(fs.statSync('client/main.jsx').isFile());
17 | assert(fs.statSync('.gitignore').isFile());
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/lib/update/unzip-design.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const logTask = require('../helpers/log-task');
3 | const DESIGN_FILE = 'design.zip';
4 | const { exec, rm } = require('../helpers/shelljs');
5 |
6 | module.exports = function unzipDesign() {
7 | // check if DESIGN_FILE exists
8 | try {
9 | const hasZipFile = fs.statSync(DESIGN_FILE).isFile();
10 | if (!hasZipFile) {
11 | return;
12 | }
13 | } catch (e) {
14 | return;
15 | }
16 |
17 | logTask('Regenerate .design/ folder');
18 |
19 | // create necessary folders
20 | rm('-rf', '.design/');
21 | exec(`unzip ${DESIGN_FILE} -d .design/`);
22 | };
23 |
--------------------------------------------------------------------------------
/lib/update/check-dot-design-dir.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 |
3 | const chalk = require('chalk');
4 | const fs = require('fs');
5 |
6 | module.exports = function checkDotDesignDir() {
7 | // check if DESIGN_FILE exists
8 | try {
9 | const hasDotDesignDir = fs.statSync('.design').isDirectory();
10 | if (!hasDotDesignDir) {
11 | throw new Error('Can not find design.zip or .design folder');
12 | }
13 | } catch (e) {
14 | console.log(chalk.red.bold(
15 | 'You need to download webflow zip file here and rename it as design.zip.'
16 | ));
17 | throw new Error('need webflow design.zip file.');
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/lib/create/install-meteor-dependencies.js:
--------------------------------------------------------------------------------
1 | const { exec } = require('../helpers/shelljs');
2 |
3 | module.exports = function installMeteorDependencies() {
4 | // meteor dependencies
5 | const meteorDependenciesToRemove = [
6 | 'autopublish',
7 | 'insecure',
8 | 'blaze-html-templates',
9 | ];
10 | exec(`meteor remove ${meteorDependenciesToRemove.join(' ')}`);
11 |
12 | const meteorDependencies = [
13 | 'accounts-base',
14 | 'static-html',
15 | 'react-meteor-data',
16 | 'aldeed:simple-schema',
17 | 'aldeed:collection2',
18 | 'dburles:collection-helpers',
19 | 'poetic:meteor-subscribe-all',
20 | ];
21 | exec(`meteor add ${meteorDependencies.join(' ')}`);
22 | };
23 |
--------------------------------------------------------------------------------
/lib/create/index.js:
--------------------------------------------------------------------------------
1 | const createMeteor = require('./create-meteor');
2 | const installMeteorDependencies = require('./install-meteor-dependencies');
3 | const installNpmDependencies = require('./install-npm-dependencies');
4 | const addScriptCommands = require('./add-script-commands');
5 | const copyBoilerplate = require('./copy-boilerplate');
6 | const logInstruction = require('./log-instruction');
7 | const plugins = require('../plugins/index');
8 |
9 | module.exports = function create(projectName, options) {
10 | createMeteor(projectName);
11 |
12 | installMeteorDependencies();
13 |
14 | installNpmDependencies();
15 |
16 | addScriptCommands();
17 |
18 | copyBoilerplate();
19 |
20 | plugins.runHook('afterCreate', options);
21 |
22 | logInstruction();
23 | };
24 |
--------------------------------------------------------------------------------
/lib/generate.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 | const path = require('path');
3 | const rt = require('reacterminator');
4 | const { ls } = require('shelljs');
5 |
6 | function getMeteorRoot() {
7 | let currentPath = path.resolve('.');
8 | do {
9 | const paths = ls('-A', currentPath);
10 | const isMeteorRoot =
11 | _.includes(paths, '.meteor') &&
12 | _.includes(paths, 'package.json');
13 |
14 | if (isMeteorRoot) {
15 | return currentPath;
16 | }
17 |
18 | currentPath = path.resolve(currentPath, '..');
19 | }
20 | while (currentPath !== '/');
21 |
22 | throw new Error('Ops, can not find your meteor project root directory.');
23 | }
24 |
25 | module.exports = (rawPath) => rt.generate(rawPath, `${getMeteorRoot()}/client/imports`);
26 |
--------------------------------------------------------------------------------
/test/integration/generate.js:
--------------------------------------------------------------------------------
1 | /* eslint-env mocha */
2 | const fs = require('fs');
3 | const { assert } = require('chai');
4 | const path = require('path');
5 | const generate = require('../../lib/generate');
6 | const { cd, rm, mkdir, touch } = require('shelljs');
7 |
8 | describe('generate', () => {
9 | it('generate a component', () => {
10 | const examplesPath = path.resolve(__dirname, '../../examples');
11 | cd(examplesPath);
12 | rm('-rf', 'example');
13 | mkdir('-p', './example/.meteor/');
14 | touch('./example/package.json');
15 | cd('example');
16 |
17 | generate('components/components/Signup');
18 |
19 | assert(fs.statSync('client/imports/custom/index.js').isFile());
20 | assert(fs.statSync('client/imports/custom/components/Signup.jsx').isFile());
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/lib/plugins/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /* List of validPlugins
3 | * Key is the argument ex: -a passed to enable this plugin
4 | * Value is the name of the folder this plugin is in
5 | * * */
6 | const validPlugins = {
7 | A: 'apollo',
8 | };
9 |
10 | /* runHook is passed a hookName 'afterCreate' and options from commander
11 | * it will search through all valid plugins and run the hookName passed
12 | * function passing in the options object */
13 | module.exports = {
14 | runHook(hookName, options = {}) {
15 | Object.keys(validPlugins).forEach((plugin) => {
16 | if (options[plugin]) {
17 | const newPlugin = require(`./${validPlugins[plugin]}/index.js`);
18 | if (typeof(newPlugin[hookName]) === 'function') {
19 | newPlugin[hookName](options);
20 | }
21 | }
22 | });
23 | },
24 | };
25 |
--------------------------------------------------------------------------------
/bin/stanza.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 | /* eslint-disable no-console */
3 |
4 | const program = require('commander');
5 | const { version, description } = require('../package.json');
6 |
7 | program
8 | .version(version)
9 | .description(description);
10 |
11 | program
12 | .command('create ', 'Create a meteor project with react and redux configured.')
13 | .alias('c');
14 |
15 | program
16 | .command('update', 'Update a meteor project with design.zip from webflow.')
17 | .alias('u');
18 |
19 | program
20 | .command('generate', 'Generate a custom file and update custom/index.js.')
21 | .alias('g');
22 |
23 | program.on('--help', () => {
24 | console.log(' Examples:');
25 | console.log('');
26 | console.log(' $ stanza c my-project');
27 | console.log(' $ stanza u');
28 | console.log('');
29 | });
30 |
31 | program.parse(process.argv);
32 |
--------------------------------------------------------------------------------
/lib/plugins/apollo/after-create.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | const { exec, cp } = require('../../helpers/shelljs');
3 | const path = require('path');
4 | const logTask = require('../../helpers/log-task');
5 |
6 | module.exports = function afterCreate() {
7 | console.log('Installing apollo dependencies');
8 | const meteorDependencies = ['apollo'];
9 |
10 | exec(`meteor add ${meteorDependencies.join(' ')}`);
11 |
12 | // npm dependencies
13 | const npmDependencies = [
14 | 'apollo-client',
15 | 'apollo-server@^0.1.1',
16 | 'http-proxy-middleware@^0.15.0',
17 | 'express',
18 | 'graphql',
19 | 'graphql-tag',
20 | 'invariant',
21 | 'react-apollo',
22 | ];
23 | exec(`meteor npm install --save ${npmDependencies.join(' ')}`);
24 |
25 | logTask('Copy Apollo boilerplate');
26 | const templatesPath = path.resolve(__dirname, './templates');
27 | cp('-R', `${templatesPath}/.`, './');
28 | };
29 |
--------------------------------------------------------------------------------
/lib/create/add-script-commands.js:
--------------------------------------------------------------------------------
1 | const logTask = require('../helpers/log-task');
2 | const path = require('path');
3 | const _ = require('lodash');
4 | const fs = require('fs');
5 |
6 | module.exports = function addScriptCommands() {
7 | // npm script commands
8 | logTask('Add npm scripts');
9 | const packageJSONPath = path.resolve('./package.json');
10 | const packageJSONObject = require(packageJSONPath); // eslint-disable-line global-require
11 | const npmScripts = {
12 | test: 'npm run lint && chimp --mocha --path=tests --browser=phantomjs',
13 | lint: 'eslint . --ext .jsx,.js',
14 | 'lint:quiet': 'eslint . --ext .jsx,.js || true',
15 | fix: 'eslint . --ext .jsx,.js --fix',
16 | watch: 'chimp --ddp=http://localhost:3000 --watch --mocha --path=tests',
17 | };
18 |
19 | _.extend(packageJSONObject.scripts, npmScripts);
20 |
21 | fs.writeFileSync(
22 | packageJSONPath, `${JSON.stringify(packageJSONObject, null, 2)}\n`
23 | );
24 | };
25 |
--------------------------------------------------------------------------------
/lib/create/install-npm-dependencies.js:
--------------------------------------------------------------------------------
1 | const { exec } = require('../helpers/shelljs');
2 |
3 | module.exports = function installNpmDependencies() {
4 | // dependencies
5 | const npmDependencies = [
6 | 'lodash',
7 | 'react',
8 | 'react-dom',
9 | 'react-addons-pure-render-mixin', // react-meteor-data depends on this
10 | 'redux',
11 | 'react-redux',
12 | 'redux-thunk',
13 | 'param-store',
14 | 'react-super-components',
15 | ];
16 | exec(`meteor npm install --save ${npmDependencies.join(' ')}`);
17 |
18 | // dev dependencies
19 | const npmDevDependencies = [
20 | 'eslint-config-poetic',
21 | 'react-addons-test-utils',
22 | 'mocha',
23 | 'faker',
24 | ];
25 | exec(`meteor npm install --save-dev ${npmDevDependencies.join(' ')}`);
26 |
27 | // chimp
28 | exec('npm list -g chimp || npm install --global chimp');
29 |
30 | // meteor-multi-deploy
31 | exec('npm install -g meteor-multi-deploy');
32 | exec('mmd setup');
33 | };
34 |
--------------------------------------------------------------------------------
/lib/update/copy-css.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 |
3 | const logTask = require('../helpers/log-task');
4 | const { mkdir, cp } = require('../helpers/shelljs');
5 | const glob = require('glob');
6 | const fs = require('fs');
7 | const _ = require('lodash');
8 | const cheerio = require('cheerio');
9 | const readOneHtml = require('../helpers/read-one-html');
10 |
11 | module.exports = function copyCss() {
12 | logTask('Regenerate css');
13 | mkdir('-p', 'client/css');
14 | mkdir('-p', 'client/css/lib');
15 | const cssFiles = glob.sync('.design/css/*.css');
16 | cssFiles.forEach((name) => {
17 | const libCssFiles = [
18 | '.design/css/normalize.css',
19 | '.design/css/webflow.css',
20 | ];
21 |
22 | if (_.includes(libCssFiles, name)) {
23 | cp(name, 'client/css/lib');
24 | } else {
25 | cp(name, 'client/css/');
26 | }
27 | });
28 | // extract css from head to main.css
29 | console.log('create client/css/main.css from html head');
30 | const styleFromHead = cheerio.load(readOneHtml())('head style').html();
31 | if (styleFromHead) {
32 | fs.writeFileSync('client/css/main.css', styleFromHead);
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/test/integration/update.js:
--------------------------------------------------------------------------------
1 | /* eslint-env mocha */
2 | const fs = require('fs');
3 | const { assert } = require('chai');
4 | const path = require('path');
5 | const update = require('../../lib/update');
6 | const { cd, rm, mkdir, cp } = require('shelljs');
7 |
8 | describe('update', () => {
9 | it('update a meteor app', () => {
10 | const examplesPath = path.resolve(__dirname, '../../examples');
11 | cd(examplesPath);
12 | rm('-rf', 'example');
13 | mkdir('./example');
14 | cp('-R', 'design', 'example/.design');
15 | cd('example');
16 | mkdir('client');
17 | cp(
18 | path.resolve(examplesPath, '../lib/create/templates/client/main.html'),
19 | 'client/main.html'
20 | );
21 |
22 | update();
23 |
24 | assert(fs.statSync('client/imports/generated/components/ComponentA.jsx').isFile());
25 | assert(fs.statSync('public/images/webflow.jpg').isFile());
26 | assert(fs.statSync('client/css/lib/webflow.css').isFile());
27 | assert(fs.statSync('public/fonts/Avenir-Book.ttf').isFile());
28 | const mainHtml = fs.readFileSync('client/main.html', 'utf-8');
29 | assert.include(mainHtml, 'WebFont.load');
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/examples/design/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Login
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
20 |
21 |
22 |
23 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "poetic-stanza",
3 | "engines": {
4 | "node": ">=6.0.0"
5 | },
6 | "description": "Poetic meteor-react-webflow project generator.",
7 | "bin": {
8 | "stanza": "bin/stanza.js",
9 | "st": "bin/stanza.js"
10 | },
11 | "scripts": {
12 | "test": "rm -rf examples/example && npm run lint && istanbul cover _mocha && coveralls < coverage/lcov.info",
13 | "lint": "eslint . --ext .jsx,.js",
14 | "fix": "eslint . --ext .jsx,.js --fix",
15 | "watch": "rm -rf examples/example && mocha --watch",
16 | "semantic-release": "semantic-release pre && npm publish && semantic-release post"
17 | },
18 | "repository": {
19 | "type": "git",
20 | "url": "https://github.com/poetic/stanza.git"
21 | },
22 | "keywords": [
23 | "meteor",
24 | "react",
25 | "webflow",
26 | "generator",
27 | "html"
28 | ],
29 | "author": "Chun-Yang",
30 | "license": "MIT",
31 | "bugs": {
32 | "url": "https://github.com/poetic/stanza/issues"
33 | },
34 | "homepage": "https://github.com/poetic/stanza",
35 | "devDependencies": {
36 | "chai": "^3.5.0",
37 | "coveralls": "^2.11.8",
38 | "eslint-config-poetic": "^1.0.2",
39 | "istanbul": "^1.0.0-alpha.2",
40 | "mocha": "^2.4.5",
41 | "semantic-release": "^4.3.5"
42 | },
43 | "dependencies": {
44 | "chalk": "^1.1.1",
45 | "cheerio": "^0.20.0",
46 | "commander": "git://github.com/tj/commander.js.git#c6236d9504b60d9a2e6aa7fc3ce17a12f48f4a3e",
47 | "glob": "^7.0.3",
48 | "lodash": "^4.6.1",
49 | "reacterminator": "^0.15.1",
50 | "shelljs": "^0.6.0"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/lib/update/copy-fonts.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 | const cheerio = require('cheerio');
3 | const readOneHtml = require('../helpers/read-one-html');
4 | const fs = require('fs');
5 | const regenerateFolderInPublic = require('../helpers/regenerate-folder-in-public');
6 |
7 | function removeFontScriptTags($, inverse) {
8 | $('head').children().each((index, element) => {
9 | const src = $(element).attr('src');
10 | const isWebFontJs = src && _.endsWith(src, 'webfont.js');
11 |
12 | const content = _.trim($(element).text());
13 | const isWebFontLoad = content && _.startsWith(content, 'WebFont.load');
14 |
15 | const isFontTag = (isWebFontJs || isWebFontLoad);
16 | const shouldRemove = inverse ? !isFontTag : isFontTag;
17 |
18 | if (shouldRemove) {
19 | $(element).remove();
20 | }
21 | });
22 | }
23 |
24 | function copyFontScriptsToMainHtml() {
25 | // get font script tags from head of a html file
26 | const $from = cheerio.load(readOneHtml());
27 | removeFontScriptTags($from, true);
28 | const tagsString = _.trim($from('head').html());
29 |
30 | // insert script tags into the the main.html
31 | const $to = cheerio.load(fs.readFileSync('client/main.html', 'utf-8'));
32 | removeFontScriptTags($to);
33 | if (tagsString) {
34 | $to('head').append(` ${tagsString}\n`);
35 | }
36 | fs.writeFileSync('client/main.html', $to.html().replace(/\n+\s*\n+/g, '\n'));
37 | }
38 |
39 | module.exports = function copyFonts() {
40 | // copy custom font files
41 | // TODO: we may need to change the css file to
42 | // import font files from an absolute url,
43 | // for now the paths are shallow, copy font files works
44 | regenerateFolderInPublic('fonts');
45 |
46 | copyFontScriptsToMainHtml();
47 | };
48 |
--------------------------------------------------------------------------------
/lib/create/templates/client/imports/custom/components/App.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import _ from 'lodash';
3 | import { Meteor } from 'meteor/meteor';
4 | import { createContainer } from 'meteor/react-meteor-data';
5 | import ParamStore from 'param-store';
6 | import subscribeAll from 'meteor/poetic:meteor-subscribe-all';
7 |
8 | export default (Base) => {
9 | class App extends React.Component {
10 | constructor (props) {
11 | super(props);
12 |
13 | this.state = {
14 | path: ParamStore.get('path')
15 | }
16 |
17 | ParamStore.listen(
18 | 'path',
19 | ({changedParams}) => {
20 | this.setState({path: changedParams['path']});
21 | }
22 | );
23 | }
24 |
25 | path () {
26 | const { loading, loggedIn } = this.props;
27 |
28 | // TODO: make sure you have 'loading' path
29 | if (loading) {
30 | return 'loading';
31 | }
32 |
33 | // TODO: put all routes that do not need authorization here
34 | const PUBLIC_ROUTES = [
35 | 'login',
36 | ];
37 |
38 | const isNotAuthorizedPath =
39 | !loggedIn && !_.includes(PUBLIC_ROUTES, this.state.path);
40 |
41 | // TODO: make sure you have 'login' path, or change login to the correct path name
42 | if (isNotAuthorizedPath) {
43 | return 'login';
44 | }
45 | }
46 |
47 | render() {
48 | return ;
49 | }
50 | }
51 |
52 | // TODO: put all subscriptions you need before rendering the app
53 | const GLOBAL_SUBSCRIPTIONS = [];
54 |
55 | const allSubscriptionsReady = subscribeAll(GLOBAL_SUBSCRIPTIONS);
56 |
57 | const AppWithData = createContainer(() => {
58 | const loggedIn = Boolean(Meteor.user())
59 |
60 | return {
61 | loading: Meteor.loggingIn() || (loggedIn && !allSubscriptionsReady.get()),
62 | loggedIn,
63 | }
64 | }, App);
65 |
66 | return AppWithData;
67 | }
68 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # stanza
2 | [![travis][travis-image]][travis-url]
3 | [![npm][npm-image]][npm-url]
4 | [![semantic-release][semantic-release-image]][semantic-release-url]
5 | [![coverall][coverall-image]][coverall-url]
6 |
7 | [travis-image]: https://travis-ci.org/poetic/stanza.svg
8 | [travis-url]: https://travis-ci.org/poetic/stanza
9 | [npm-image]: https://img.shields.io/npm/v/poetic-stanza.svg
10 | [npm-url]: https://npmjs.org/package/poetic-stanza
11 | [semantic-release-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
12 | [semantic-release-url]: https://github.com/semantic-release/semantic-release
13 | [coverall-image]: https://coveralls.io/repos/github/poetic/stanza/badge.svg?branch=master
14 | [coverall-url]: https://coveralls.io/github/poetic/stanza
15 |
16 | ## Requirements
17 | * node >= 6.0.0
18 | * java development kit must be installed to run tests (chimp uses selenium)
19 |
20 | ## Usage
21 |
22 | ### Alias
23 | `st`
24 |
25 | ### Example
26 | ```
27 | npm install -g poetic-stanza
28 | stanza create magic
29 | cd magic
30 |
31 | // dowload your webflow project as a zip file
32 | // copy into current directory as design.zip
33 | cp ~/Downloads/magic.zip ./design.zip
34 |
35 | stanza update
36 | ```
37 |
38 | ### [Demo App](https://github.com/poetic/stanza-demo)
39 |
40 | ### CLI
41 | ```
42 | Usage: stanza [options] [command]
43 |
44 |
45 | Commands:
46 |
47 | create|c Create a meteor project with react and redux configured.
48 | update|u Update a meteor project with design.zip from webflow.
49 | help [cmd] display help for [cmd]
50 |
51 | Poetic meteor-react-webflow project generator.
52 |
53 | Options:
54 |
55 | -h, --help output usage information
56 | -V, --version output the version number
57 |
58 | Examples:
59 |
60 | $ stanza c my-project
61 | $ stanza u
62 | ```
63 |
64 | ### travis-ci
65 | Go to [travis-ci](https://travis-ci.com/) enable the repo.
66 | Remember to push a commit after you do this.
67 |
68 | ## Development
69 |
70 | ### [Trello](https://trello.com/b/WUNN44Dp/stanza)
71 |
--------------------------------------------------------------------------------