diff --git a/app/templates/_babelrc b/app/templates/_babelrc new file mode 100644 index 00000000..c71d3666 --- /dev/null +++ b/app/templates/_babelrc @@ -0,0 +1,4 @@ +{ + "plugins": ["transform-runtime"], + "presets": ["es2015", "stage-0"] +} diff --git a/app/templates/_jshintrc b/app/templates/_jshintrc index d80a4fff..d4ce9de7 100644 --- a/app/templates/_jshintrc +++ b/app/templates/_jshintrc @@ -1,6 +1,7 @@ { "browser": true, "camelcase": true, + "esversion": 6, "curly": true, "devel": true, "eqeqeq": true, @@ -11,7 +12,7 @@ "quotmark": "single", "undef": true, "unused": "vars", - "strict": true, + "strict": "implied", "trailing": true, "globals": { "angular": false, diff --git a/app/templates/bower.json b/app/templates/bower.json index 25a254de..d253c8a8 100644 --- a/app/templates/bower.json +++ b/app/templates/bower.json @@ -1,48 +1,7 @@ { "name": "@sample-app-name", "version": "0.0.0", - "dependencies": { - "jquery": "~2.1.4", - "angular": "~1.4.4", - "angular-animate": "~1.4.4", - "angular-bootstrap": "^1.1", - "angular-bootstrap-confirm": "^2.3.0", - "angular-cookies": "~1.4.4", - "angular-google-maps": "2.3.2", - "angular-highlightjs": "~0.4.3", - "angular-messages": "~1.4.4", - "angular-mocks": "~1.4.4", - "angular-sanitize": "~1.4.4", - "angular-ui-router": "~0.2.15", - "angular-ui-tinymce": "~0.0.9", - "angular-x2js": "https://github.com/janmichaelyu/angular-x2js.git", - "bootstrap": "~3.3.5", - "font-awesome": "~4.6.0", - "highcharts": "^4.2", - "highlightjs":"~8.7.0", - "ng-json-explorer": "8c2a0f9104", - "ngtoast": "^2.0.0", - "lodash": "~3.10.1", - "ml-search-ng": "~0.2.0", - "ml-utils": "withjam/ml-utils", - "tinymce-dist": "4.3.12", - "vkbeautify-wrapper": "*" - }, - "overrides": { - "angular-highlightjs": { - "dependencies": { - "angular" : ">1.0.8", - "highlightjs":"~8.7.0" - } - } - }, - "devDependencies": { - "angular-mocks": "~1.4.4", - "bardjs": "~0.1.8", - "sinon": "http://sinonjs.org/releases/sinon-1.16.1.js" - }, - "private": true, - "resolutions": { - "angular": "~1.4.4" - } + "dependencies": {}, + "overrides": {}, + "devDependencies": {} } diff --git a/app/templates/gulp.config.js b/app/templates/gulp.config.js index fa67009b..286f87e9 100644 --- a/app/templates/gulp.config.js +++ b/app/templates/gulp.config.js @@ -12,20 +12,24 @@ module.exports = function() { var temp = './.tmp/'; var _ = require('lodash'); var wiredep = require('wiredep'); + var bower = { json: require('./bower.json'), directory: './bower_components/', ignorePath: '..' }; + var getWiredepDefaultOptions = function() { return { bowerJson: bower.json, directory: bower.directory, ignorePath: bower.ignorePath, - exclude: [ 'requirejs', 'angularjs', 'font-awesome.css' ] + exclude: ['requirejs', 'angularjs', 'font-awesome.css'] }; }; - var bowerFiles = wiredep(_.merge(getWiredepDefaultOptions(), { devDependencies: true })).js; + var bowerFiles = wiredep(_.merge(getWiredepDefaultOptions(), { + devDependencies: true + })).js; var nodeModules = 'node_modules'; var config = { @@ -94,7 +98,9 @@ module.exports = function() { /** * plato */ - plato: {js: clientApp + '**/*.js'}, + plato: { + js: clientApp + '**/*.js' + }, /** * browser sync @@ -183,9 +189,15 @@ module.exports = function() { // dir: report + 'coverage', reporters: [ // reporters not supporting the `file` property - {type: 'html', subdir: 'report-html'}, - {type: 'lcov', subdir: 'report-lcov'}, - {type: 'text-summary'} //, subdir: '.', file: 'text-summary.txt'} + { + type: 'html', + subdir: 'report-html' + }, { + type: 'lcov', + subdir: 'report-lcov' + }, { + type: 'text-summary' + } //, subdir: '.', file: 'text-summary.txt'} ] }, preprocessors: {} diff --git a/app/templates/gulpfile.js b/app/templates/gulpfile.js index 147a39a7..08de65d2 100644 --- a/app/templates/gulpfile.js +++ b/app/templates/gulpfile.js @@ -1,4 +1,4 @@ -/*jshint node: true */ +/*jshint node: true, strict: true*/ 'use strict'; @@ -12,6 +12,12 @@ var gulp = require('gulp'); var path = require('path'); var username = require('username'); +var webpack = require('webpack'); +var webpackDevMiddelware = require('webpack-dev-middleware'); +var webpachHotMiddelware = require('webpack-hot-middleware'); +var colorsSupported = require('supports-color'); +var historyApiFallback = require('connect-history-api-fallback'); + /* jshint ignore:start */ var _ = require('lodash'); var $ = require('gulp-load-plugins')({ @@ -19,14 +25,6 @@ var $ = require('gulp-load-plugins')({ }); /* jshint ignore:end */ -var _s = require('underscore.string'), - q = require('q'), - spawn = require('child_process').spawn; - -var encoding = { - encoding: 'utf8' -}; - /** * yargs variables can be passed in to alter the behavior, when present. * Example: gulp serve-dev @@ -76,188 +74,115 @@ gulp.task('plato', function(done) { startPlatoVisualizer(done); }); -/** - * Compile less to css - * @return {Stream} - */ -gulp.task('styles', ['clean-styles'], function() { - log('Compiling Less --> CSS'); - - var less = $.less().on('error', function(e) { - $.util.log($.util.colors.red(e)); - this.emit('end', e); - }); - - return gulp - .src(config.mainLess) - .pipe($.plumber()) // exit gracefully if something fails after this - .pipe(less) - .pipe($.autoprefixer({ - browsers: ['last 2 version', '> 5%'] - })) - .pipe(gulp.dest(config.temp)) - .pipe($.if(args.verbose, $.print())); -}); - -/** - * Copy fonts - * @return {Stream} - */ -gulp.task('fonts', ['clean-fonts'], function() { - log('Copying fonts'); - - return gulp - .src(config.fonts) - .pipe(gulp.dest(config.client + 'fonts')) - .pipe($.if(args.verbose, $.print())) - .pipe(gulp.dest(config.build + 'fonts')) - .pipe($.if(args.verbose, $.print())); -}); +var root = 'ui'; +// helper method for resolving paths +var resolveToApp = function(glob) { + return path.join(root, 'app', glob); // app/{glob} +}; -/** -- * Copy static data like lang.json -- * @return {Stream} -- */ -gulp.task('statics', function() { - log('Copying statics'); +var resolveToComponents = function(glob) { + return path.join(root, 'app/components', glob); // app/components/{glob} +}; - return gulp - .src(config.staticdata) - .pipe(gulp.dest(config.build)) - .pipe($.if(args.verbose, $.print())); -}); +// map of all paths +var paths = { + js: resolveToComponents('**/*!(.spec.js).js'), // exclude spec files + html: [ + resolveToApp('**/*.html'), + path.join(root, 'index.html') + ], + entry: [ + 'babel-polyfill', + path.join(__dirname, root, 'app/app.js') + ], + output: root, + blankTemplates: path.join(__dirname, 'generator', 'component/**/*.**'), + dest: path.join(__dirname, 'dist') +}; -/** - * Copy tinymce files - * @return {Stream} - */ -gulp.task('tinymce', function() { - log('Copying tinymce files'); - return gulp - .src(config.tinymce, { - base: './bower_components/tinymce-dist' - }) - .pipe(gulp.dest(config.build + 'js/')) - .pipe($.if(args.verbose, $.print())); -}); +// use webpack.config.js to build modules +gulp.task('build', ['clean', 'test'], function(cb) { + var config = require('./webpack/webpack.dist.config'); + config.entry.app = paths.entry; -/** - * Compress images - * @return {Stream} - */ -gulp.task('images', ['clean-images'], function() { - log('Compressing and copying images'); + webpack(config, function(err, stats) { + if (err) { + throw new $.util.PluginError('webpack', err); + } - return gulp - .src(config.images) - .pipe($.imagemin({ - optimizationLevel: 4 - })) - .pipe(gulp.dest(config.build + 'images')) - .pipe($.if(args.verbose, $.print())); -}); + $.util.log('[webpack]', stats.toString({ + colors: colorsSupported, + chunks: false, + errorDetails: true + })); -/** - * Watch for less file changes - * @return {Stream} - */ -gulp.task('less-watcher', function() { - return gulp.watch([config.less], ['styles']); + cb(); + }); }); -/** - * Create $templateCache from the html templates - * @return {Stream} - */ -gulp.task('templatecache', ['clean-code'], function() { - log('Creating an AngularJS $templateCache'); - - return gulp - .src(config.htmltemplates) - .pipe($.if(args.verbose, $.bytediff.start())) - .pipe($.htmlmin()) - .pipe($.if(args.verbose, $.bytediff.stop(bytediffFormatter))) - .pipe($.angularTemplatecache( - config.templateCache.file, - config.templateCache.options - )) - .pipe(gulp.dest(config.temp)) - .pipe($.if(args.verbose, $.print())); -}); /** - * Wire-up the bower dependencies - * @return {Stream} + * Creates a sample local.json; can be used as model for dev.json and prod.json */ -gulp.task('wiredep', function() { - log('Wiring the bower dependencies into the html'); - - var wiredep = require('wiredep').stream; - var options = config.getWiredepDefaultOptions(); - - // Only include stubs if flag is enabled - var js = args.stubs ? [].concat(config.js, config.stubsjs) : config.js; +gulp.task('init-local', function() { + //copy from slushfile - config gulp - with modifications to use config instead + log('Creating local.json sample document with values drawn from gulp.config.js'); + try { + var configJSON = {}; + configJSON['ml-version'] = config.marklogic.version; + configJSON['ml-host'] = config.marklogic.host; + configJSON['ml-admin-user'] = config.marklogic.username; + configJSON['ml-admin-pass'] = config.marklogic.password; + configJSON['ml-app-user'] = config.marklogic.username; + configJSON['ml-app-pass'] = config.marklogic.password; + configJSON['ml-http-port'] = config.marklogic.httpPort; + configJSON['node-port'] = config.defaultPort; + + if (config.marklogic.version < 8) { + configJSON['ml-xcc-port'] = config.marklogic.xccPort; + } - return gulp - .src(config.index) - .pipe(wiredep(options)) - .pipe(inject(js, '', config.jsOrder)) - .pipe(gulp.dest(config.client)) - .pipe($.if(args.verbose, $.print())); + var configString = JSON.stringify(configJSON, null, 2) + '\n'; + fs.writeFileSync('local.json', configString, { + encoding: 'utf8' + }); + } catch (e) { + log('failed to write local.json: ' + e.message); + } }); /** - * Inject dependencies into index.html - * @return {Stream} + * Updates ecosystem.json */ -gulp.task('inject', ['wiredep', 'styles', 'templatecache'], function() { - log('Wire up css into the html, after files are ready'); - - return gulp - .src(config.index) - .pipe(inject(config.css)) - .pipe(gulp.dest(config.client)) - .pipe($.if(args.verbose, $.print())); -}); +gulp.task('add-deploy-target', function(done) { + log('Update ecosystem.json targets or create new ones!'); -/** - * Initialize config files for local environment - */ -gulp.task('init-local', function(done) { - init('local', done); -}); + var ecosystem = 'ecosystem.json'; -/** - * Initialize config files for dev environment - */ -gulp.task('init-dev', function(done) { - init('dev', done); -}); + if (!fs.existsSync(ecosystem)) { + try { + var configJSON = {}; + configJSON.deploy = {}; -/** - * Initialize config files for prod environment - */ -gulp.task('init-prod', function(done) { - init('prod', done); -}); + var configString = JSON.stringify(configJSON, null, 2) + '\n'; -/** - * Updates ecosystem.json - */ -gulp.task('add-deploy-target', function(done) { - log('Update ecosystem.json targets or create new ones!'); + fs.writeFileSync('ecosystem.json', configString, { + encoding: 'utf8' + }); + } catch (e) { + console.log('failed to write ecosystem.json: ' + e.message); + } + } - var properties = fs.readFileSync('deploy/build.properties', encoding); + var properties = fs.readFileSync('deploy/build.properties', { + encoding: 'utf8' + }); var name = properties.match(/app-name=(.*)/)[1]; var gitUrl = 'https://github.com/'; var folderPath = '/space/projects/' + name; - var ecosystem = 'ecosystem.json'; - - ecosystemMustExist(ecosystem, name); - $.git.exec({ args: 'config --get remote.origin.url', quiet: true @@ -301,22 +226,11 @@ gulp.task('add-deploy-target', function(done) { name: 'folder', message: 'Where do you want to store the project on your target server?', default: folderPath - }, { - type: 'list', - name: 'local', - message: 'Is there any sensitive information or credentials here that shouldn\'t go in source control?', - choices: ['no', 'yes'], - default: 0 }]; gulp.src(ecosystem) .pipe($.prompt.prompt(questions, function(answers) { - if (answers.local === 'yes') { - ecosystem = 'local.ecosystem.json'; - ecosystemMustExist(ecosystem, name); - } - gulp.src(ecosystem) .pipe($.jsonEditor(function(json) { json.deploy[answers.targetName] = { @@ -326,7 +240,7 @@ gulp.task('add-deploy-target', function(done) { 'ref': 'origin/' + answers.branch, 'repo': answers.gitUrl, 'path': answers.folder, - 'post-deploy': 'npm install && bower install && gulp build' + 'post-deploy': 'npm install; bower install; gulp build' }; return json; // must return JSON object. })) @@ -335,222 +249,17 @@ gulp.task('add-deploy-target', function(done) { }); }); -function ecosystemMustExist(ecosystem, name) { - if (!fs.existsSync(ecosystem)) { - try { - var configJSON = { - 'apps': [{ - 'name': name, - 'script': './node-server/node-app.js', - 'watch': true, - 'restart_delay': 4000, - 'env': { - 'NODE_ENV': 'local' - }, - 'env_local': { - 'NODE_ENV': 'local' - }, - 'env_dev': { - 'NODE_ENV': 'dev' - }, - 'env_prod': { - 'NODE_ENV': 'prod' - } - }], - 'deploy': {} - }; - - var configString = JSON.stringify(configJSON, null, 2) + '\n'; - - fs.writeFileSync(ecosystem, configString, encoding); - } catch (e) { - console.log('failed to write ecosystem.json: ' + e.message); - } - } -} - -/** - * Run the spec runner - * @param {Function} done - callback when complete - */ -gulp.task('serve-specs', ['build-specs'], function(done) { - log('run the spec runner'); - serve('local' /* env */ , true /* specRunner */ ); - done(); -}); - -/** - * Inject all the spec files into the specs.html - * @return {Stream} - */ -gulp.task('build-specs', ['templatecache'], function() { - log('building the spec runner'); - - var wiredep = require('wiredep').stream; - var templateCache = config.temp + config.templateCache.file; - var options = config.getWiredepDefaultOptions(); - var specs = config.specs; - - if (args.startServers) { - specs = [].concat(specs, config.serverIntegrationSpecs); - } - options.devDependencies = true; - - return gulp - .src(config.specRunner) - .pipe(wiredep(options)) - .pipe(inject(config.js, '', config.jsOrder)) - .pipe(inject(config.testlibraries, 'testlibraries')) - .pipe(inject(config.specHelpers, 'spechelpers')) - .pipe(inject(specs, 'specs', ['**/*'])) - .pipe(inject(templateCache, 'templates')) - .pipe(gulp.dest(config.client)) - .pipe($.if(args.verbose, $.print())); -}); - -/** - * Build everything - * This is separate so we can run tests on - * optimize before handling image or fonts - * @param {Function} done - callback when complete - */ -gulp.task('build', ['optimize', 'images', 'fonts', 'statics', 'tinymce'], function(done) { - log('Building everything'); - - var msg = { - title: 'gulp build', - subtitle: 'Deployed to the dist folder', - message: 'Ready to run `gulp serve-dev` or `gulp serve-prod`' - }; - log(msg); - notify(msg); - done(); -}); - -/** - * Optimize all files, move to a build folder, - * and inject them into the new index.html - * @return {Stream} - */ -gulp.task('optimize', ['inject', 'test'], function() { - log('Optimizing the js, css, and html'); - - // Filters are named for the gulp-useref path - var restore = { - restore: true - }; - var cssFilter = $.filter('**/*.css', restore); - var jsAppFilter = $.filter('**/' + config.optimized.app, restore); - var jslibFilter = $.filter('**/' + config.optimized.lib, restore); - - var templateCache = config.temp + config.templateCache.file; - - var combined = gulp - .src(config.index) - .pipe($.plumber()) - .pipe(inject(templateCache, 'templates')) - // Apply the concat and file replacement with useref - .pipe($.useref({ - searchPath: './' - })) - // Get the css - .pipe(cssFilter) - // Take inventory of the css file names for future rev numbers - .pipe($.rev()) - .pipe($.sourcemaps.init()) - .pipe($.cssnano({ - safe: true - })) - // write sourcemap for css - .pipe($.sourcemaps.write('.')) - .pipe(cssFilter.restore) - // Get the custom javascript - .pipe(jsAppFilter) - // Take inventory of the js app file name for future rev numbers - .pipe($.rev()) - .pipe($.sourcemaps.init()) - .pipe($.ngAnnotate({ - add: true - })) - .pipe($.uglify()) - // write sourcemap for js app - .pipe($.sourcemaps.write('.')) - .pipe(jsAppFilter.restore) - // Get the vendor javascript - .pipe(jslibFilter) - // Take inventory of the js lib file name for future rev numbers - .pipe($.rev()) - .pipe($.sourcemaps.init()) - .pipe($.uglify()) // another option is to override wiredep to use min files - // write sourcemap for js lib - .pipe($.sourcemaps.write('.')) - .pipe(jslibFilter.restore) - // Rename the recorded file names in the steam, and in the html to append rev numbers - .pipe($.revReplace()) - // copy result to dist/, and print some logging.. - .pipe(gulp.dest(config.build)) - .pipe($.if(args.verbose, $.print())); - - combined.on('error', console.error.bind(console)); - - return combined; -}); - /** * Remove all files from the build, temp, and reports folders * @return {Stream} */ -gulp.task('clean', ['clean-fonts'], function() { - var files = [].concat(config.build, config.temp, config.report); - return clean(files); -}); - -/** - * Remove all fonts from the build folder - * @return {Stream} - */ -gulp.task('clean-fonts', function() { - var files = [].concat( - config.build + 'fonts/**/*.*', - config.client + 'fonts/**/*.*' - ); - return clean(files); -}); - -/** - * Remove all images from the build folder - * @return {Stream} - */ -gulp.task('clean-images', function() { - return clean(config.build + 'images/**/*.*'); +gulp.task('clean', function(cb) { + del([paths.dest]).then(function(paths) { + $.util.log('[clean]', paths); + cb(); + }); }); -/** - * Remove all styles from the build and temp folders - * @return {Stream} - */ -gulp.task('clean-styles', function() { - var files = [].concat( - config.temp + '**/*.css', - config.build + 'styles/**/*.css', - config.build + 'styles/**/*.css.map' - ); - return clean(files); -}); - -/** - * Remove all js and html from the build and temp folders - * @return {Stream} - */ -gulp.task('clean-code', function() { - var files = [].concat( - config.temp + '**/*.js', - config.build + 'js/**/*.js', - config.build + 'js/**/*.js.map', - config.build + '**/*.html' - ); - return clean(files); -}); /** * Run specs once and exit @@ -558,7 +267,7 @@ gulp.task('clean-code', function() { * gulp test --startServers * @param {Function} done - callback when complete */ -gulp.task('test', ['vet', 'templatecache'], function(done) { +gulp.task('test', ['vet'], function(done) { startTests(true /*singleRun*/ , done); }); @@ -579,7 +288,7 @@ gulp.task('autotest', function(done) { * --nosync * @return {Stream} */ -gulp.task('serve-local', ['inject', 'fonts'], function() { +gulp.task('serve-local', function() { return serve('local' /*env*/ ); }); @@ -634,260 +343,6 @@ gulp.task('bump', function() { .pipe($.if(args.verbose, $.print())); }); -/** - * When files change, log it - * @param {Object} event - event that fired - */ -function changeEvent(event) { - var srcPattern = new RegExp('/.*(?=/' + config.source + ')/'); - log('File ' + event.path.replace(srcPattern, '') + ' ' + event.type); -} - -/** - * Delete all files in a given path - * @param {Array} files - array of paths to delete - * @return {Stream} - */ -function clean(files) { - log('Cleaning: ' + $.util.colors.blue(files)); - return del(files) - .then(function(paths) { - if (args.verbose) { - log(paths.map(function(path) { - return path.replace(__dirname + '/', 'rm '); - })); - } - }); -} - -/** - * Initialize config files for given env - * @param {String} env The environment name (local, dev, prod) - * @param {Function} done - callback when complete - */ -function init(env, done) { - if (fs.existsSync(env + '.json')) { - log('NOTE: ' + env + '.json already exists, change manually if needed.'); - if (fs.existsSync('deploy/' + env + '.properties')) { - log('NOTE: deploy/' + env + '.properties already exists, change manually too.'); - } else { - log('WARN: deploy/' + env + '.properties is missing!'); - } - done(); - } else { - //copy from slushfile - config gulp - with modifications to use config instead - var inquirer = require('inquirer'); - - run('./ml', [env, 'info', '--format=json']).then(function(output) { - var localJson = fs.existsSync('local.json') ? JSON.parse(fs.readFileSync('local.json', 'utf8')) : {}; - - var localAppName = localJson['app-name']; - var localMlVersion = localJson['ml-version']; - var localMlHost = localJson['ml-host']; - var localMlAdminUser = localJson['ml-admin-user']; - var localMlAppUser = localJson['ml-app-user']; - var localMlAppPass = localJson['ml-app-pass']; - var localMlHttpPort = localJson['ml-http-port']; - var localMlXccPort = localJson['ml-xcc-port']; - var localNodePort = localJson['node-port'] || 9070; - var localGuestAccess = ['false', 'true'].indexOf(localJson['guest-access']); - var localDisallowUpdates = ['false', 'true'].indexOf(localJson['disallow-updates']); - var localAppUsersOnly = ['false', 'true'].indexOf(localJson['appusers-only']); - - var properties = JSON.parse(output).properties || {}; - - var mlVersion = ['8', '7', '6', '5'].indexOf(localMlVersion || properties['ml.server-version'] || '8'); - var marklogicHost = properties['ml.' + env + '-server'] || localMlHost || 'localhost'; - var marklogicAdminUser = properties['ml.user'] || localMlAdminUser || 'admin'; - var appName = properties['ml.app-name'] || localAppName; - var appUserName = properties['ml.default-user'] || localMlAppUser; - var appUserPass = unescape(properties['ml.appuser-password']) || localMlAppPass; - var appPort = localMlHttpPort || properties['ml.app-port'] || 8040; - var xccPort = localMlXccPort || properties['ml.xcc-port'] || 8041; - - var prompts = [{ - type: 'list', - name: 'mlVersion', - message: 'MarkLogic version?', - choices: ['8', '7', '6', '5'], - default: mlVersion > 0 ? mlVersion : 0 - }, { - type: 'input', - name: 'marklogicHost', - message: 'MarkLogic Host?', - default: marklogicHost - }, { - type: 'input', - name: 'marklogicAdminUser', - message: 'MarkLogic Admin User?', - default: marklogicAdminUser - }, { - type: 'input', - name: 'marklogicAdminPass', - message: 'Note: consider keeping the following blank, ' + - 'you will be asked to enter it at appropriate commands.\n? MarkLogic Admin Password?', - default: '' - }, { - type: 'input', - name: 'appPort', - message: 'MarkLogic App/Rest port?', - default: appPort - }, { - type: 'input', - name: 'xccPort', - message: 'XCC port?', - default: xccPort, - when: function(answers) { - return answers.mlVersion < 8; - } - }, { - type: 'input', - name: 'nodePort', - message: 'Node app port?', - default: localNodePort - }, { - type: 'list', - name: 'guestAccess', - message: 'Allow anonymous users to search data?', - choices: ['false', 'true'], - default: localGuestAccess > 0 ? localGuestAccess : 0 - }, { - type: 'list', - name: 'disallowUpdates', - message: 'Disallow proxying update requests?', - choices: ['false', 'true'], - default: localDisallowUpdates > 0 ? localDisallowUpdates : 0 - }, { - type: 'list', - name: 'appUsersOnly', - message: 'Only allow access to users created for this app? Note: disallows admin users.', - choices: ['false', 'true'], - default: localAppUsersOnly > 0 ? localAppUsersOnly : 0 - }]; - - if (typeof appName === 'undefined') { - prompts.unshift({ - type: 'input', - name: 'name', - message: 'Name for the app?' - }); - } - - inquirer.prompt(prompts, function(settings) { - if (typeof appName === 'undefined') { - settings.nameDashed = _s.slugify(settings.name); - } else { - settings.nameDashed = _s.slugify(appName); - } - - try { - var configJSON = {}; - configJSON['app-name'] = settings.nameDashed; - configJSON['ml-version'] = settings.mlVersion; - configJSON['ml-host'] = settings.marklogicHost; - configJSON['ml-admin-user'] = settings.marklogicAdminUser; - configJSON['ml-admin-pass'] = settings.marklogicAdminPass; - configJSON['ml-app-user'] = appUserName || (settings.nameDashed + '-user'); - configJSON['ml-app-pass'] = appUserPass || ''; - configJSON['ml-http-port'] = settings.appPort; - - if (settings.mlVersion < 8) { - configJSON['ml-xcc-port'] = settings.xccPort; - } - - configJSON['node-port'] = settings.nodePort; - configJSON['guest-access'] = settings.guestAccess; - configJSON['disallow-updates'] = settings.disallowUpdates; - configJSON['appusers-only'] = settings.appUsersOnly; - - var configString = JSON.stringify(configJSON, null, 2) + '\n'; - fs.writeFileSync(env + '.json', configString, encoding); - log('Created ' + env + '.json.'); - - if (fs.existsSync('deploy/' + env + '.properties')) { - log('NOTE: deploy/' + env + '.properties already exists, change manually please!'); - } else { - var envProperties = '#################################################################\n' + - '# This file contains overrides to values in build.properties\n' + - '# These only affect your local environment and should not be checked in\n' + - '#################################################################\n' + - '\n' + - 'server-version=' + settings.mlVersion + '\n' + - '\n' + - '#\n' + - '# The ports used by your application\n' + - '#\n' + - 'app-port=' + settings.appPort + '\n'; - if (settings.mlVersion < 8) { - envProperties += 'xcc-port=' + settings.xccPort + '\n'; - } else { - envProperties += '# Taking advantage of not needing a XCC Port for ML8\n' + - 'xcc-port=${app-port}\n' + - 'install-xcc=false\n'; - } - - envProperties += '\n' + - '#\n' + - '# the uris or IP addresses of your servers\n' + - '# WARNING: if you are running these scripts on WINDOWS you may need to change localhost to 127.0.0.1\n' + - '# There have been reported issues with dns resolution when localhost wasn\'t in the hosts file.\n' + - '#\n' + - env + '-server=' + settings.marklogicHost + '\n' + - 'content-forests-per-host=3\n' + - '\n' + - '#\n' + - '# Admin username/password that will exist on the local/dev/prod servers\n' + - '#\n' + - 'user=' + settings.marklogicAdminUser + '\n' + - 'password=' + settings.marklogicAdminPass + '\n'; - - fs.writeFileSync('deploy/' + env + '.properties', envProperties, encoding); - log('Created deploy/' + env + '.properties.'); - } - done(); - } catch (e) { - log('Failed to write ' + env + ' config files: ' + e.message); - done(); - } - }); - }); - } -} -// bypass Roxy bug that causes special XML chars to get escaped as entities -function unescape(s) { - return s.replace(''', '\'').replace('"', '"').replace('<', '<').replace('>', '>').replace('&', '&').replace('{{', '{').replace('}}', '}'); -} - -/** - * Inject files in a sorted sequence at a specified inject label - * @param {Array} src glob pattern for source files - * @param {String} label The label name - * @param {Array} order glob pattern for sort order of the files - * @return {Stream} - */ -function inject(src, label, order) { - var options = { - read: false - }; - if (label) { - options.name = 'inject:' + label; - } - - return $.inject(orderSrc(src, order), options); -} - -/** - * Order a stream - * @param {Stream} src The gulp.src stream - * @param {Array} order Glob array pattern - * @return {Stream} The ordered stream - */ -function orderSrc(src, order) { - return gulp - .src(src) - .pipe($.if(order, $.order(order))); -} - /** * serve the code * --debug-brk or --debug @@ -954,7 +409,7 @@ function getNodeOptions(env) { delayTime: 1, env: { 'PORT': port, - 'NODE_ENV': env, + 'NODE_ENV': isDevMode(env) ? 'dev' : 'build', 'APP_PORT': port, 'ML_HOST': args['ml-host'] || process.env.ML_HOST || envJson['ml-host'] || config.marklogic.host, 'ML_APP_USER': args['ml-app-user'] || process.env.ML_APP_USER || envJson['ml-app-user'] || config.marklogic.user, @@ -967,11 +422,17 @@ function getNodeOptions(env) { }; } -/** - * Start BrowserSync - * --nosync will avoid browserSync - */ -function startBrowserSync(env, specRunner) { +function startBrowserSync(env) { + var config = require('./webpack/webpack.dev.config'); + config.entry.app = [ + // this modules required to make HRM working + // it responsible for all this webpack magic + 'webpack-hot-middleware/client?reload=true', + // application entry point + ].concat(paths.entry); + + var compiler = webpack(config); + var nodeOptions = getNodeOptions(env); if (args.nosync || browserSync.active) { @@ -980,23 +441,27 @@ function startBrowserSync(env, specRunner) { log('Starting BrowserSync on port ' + nodeOptions.env.APP_PORT); - // If build: watches the files, builds, and restarts browser-sync. - // If dev: watches less, compiles it to css, browser-sync handles reload - if (isDevMode(env)) { - gulp.watch([config.less], ['styles']) - .on('change', changeEvent); - } else { - gulp.watch([config.less, config.js, config.html], ['optimize', browserSync.reload]) - .on('change', changeEvent); - } - var options = { proxy: 'localhost:' + nodeOptions.env.APP_PORT, port: 3000, - files: isDevMode(env) ? [ - config.client + '**/*.*', - '!' + config.less, - config.temp + '**/*.css' + notify: true, + reloadDelay: 0, //1000 + ui: false, + open: true, + /*server: { + baseDir: root + },*/ + middleware: isDevMode(env) ? [ + historyApiFallback(), + webpackDevMiddelware(compiler, { + stats: { + colors: colorsSupported, + chunks: false, + modules: false + }, + publicPath: config.output.publicPath + }), + webpachHotMiddelware(compiler) ] : [], ghostMode: { // these are the defaults t,f,t,t clicks: true, @@ -1007,14 +472,8 @@ function startBrowserSync(env, specRunner) { injectChanges: true, logFileChanges: true, logLevel: 'debug', - logPrefix: 'gulp-patterns', - notify: true, - reloadDelay: 0, //1000 - ui: false + logPrefix: 'gulp-patterns' }; - if (specRunner) { - options.startPath = config.specRunnerFile; - } browserSync(options); } @@ -1064,7 +523,7 @@ function startTests(singleRun, done) { if (args.startServers) { log('Starting servers'); var savedEnv = process.env; - savedEnv.NODE_ENV = 'local'; + savedEnv.NODE_ENV = 'dev'; savedEnv.PORT = 8888; child = fork(config.nodeServer); } else { @@ -1099,29 +558,6 @@ function startTests(singleRun, done) { } } -/** - * Formatter for bytediff to display the size changes after processing - * @param {Object} data - byte data - * @return {String} Difference in bytes, formatted - */ -function bytediffFormatter(data) { - var difference = (data.savings > 0) ? ' smaller.' : ' larger.'; - return data.fileName + ' went from ' + - (data.startSize / 1000).toFixed(2) + ' kB to ' + - (data.endSize / 1000).toFixed(2) + ' kB and is ' + - formatPercent(1 - data.percent, 2) + '%' + difference; -} - -/** - * Format a number as a percentage - * @param {Number} num Number to format as a percent - * @param {Number} precision Precision of the decimal - * @return {String} Formatted perentage - */ -function formatPercent(num, precision) { - return (num * 100).toFixed(precision); -} - /** * Log a message or series of messages using chalk's blue color. * Can pass in a string, object or array. @@ -1138,50 +574,4 @@ function log(msg) { } } -/** - * Show OS level notification using node-notifier - */ -function notify(options) { - var notifier = require('node-notifier'); - var notifyOptions = { - sound: 'Bottle', - contentImage: path.join(__dirname, 'gulp.png'), - icon: path.join(__dirname, 'gulp.png') - }; - _.assign(notifyOptions, options); - notifier.notify(notifyOptions); -} - -function run(cmd, args, verbose) { - var d = q.defer(); - var output = ''; - - console.log('Spawning ' + cmd + ' ' + args.join(' ')); - var child = spawn(cmd, args, { - stdio: [ - 0, // Use parents stdin for child - 'pipe', // Pipe child's stdout to parent (default) - 'pipe' // Pipe child's stderr to parent (default) - ] - }); - - child.on('close', function() { - console.log('done running ' + cmd); - d.resolve(output); - }); - - child.stdout.on('data', function(chunk) { - if (verbose) { - console.log(chunk.toString()); - } - output += chunk.toString(); - }); - - child.stderr.on('data', function(data) { - console.log(data.toString()); - }); - - return d.promise; -} - module.exports = gulp; diff --git a/app/templates/jsconfig.json b/app/templates/jsconfig.json new file mode 100644 index 00000000..69ca405e --- /dev/null +++ b/app/templates/jsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "ES6" + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/app/templates/karma.conf.js b/app/templates/karma.conf.js index 6635f9b0..8f992aa4 100644 --- a/app/templates/karma.conf.js +++ b/app/templates/karma.conf.js @@ -1,66 +1,130 @@ /*jshint node: true */ - -'use strict'; -/*global module*/ -// Karma configuration -// http://karma-runner.github.io/0.10/config/configuration-file.html - module.exports = function(config) { - var gulpConfig = require('./gulp.config')(); - config.set({ - // base path that will be used to resolve all patterns (eg. files, exclude) - basePath: './', + // base path used to resolve all patterns + basePath: '', // frameworks to use - // some available frameworks: https://npmjs.org/browse/keyword/karma-adapter + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter frameworks: ['mocha', 'chai', 'sinon', 'chai-sinon'], - // list of files / patterns to load in the browser - files: gulpConfig.karma.files, + // list of files/patterns to load in the browser + files: [{ + pattern: 'spec.bundle.js', + watched: false + }], + + // files to exclude + exclude: [], + + plugins: [ + require('karma-chai'), + require('karma-chrome-launcher'), + require('karma-mocha'), + require('karma-mocha-reporter'), + require('karma-coverage'), + require('karma-notify-reporter'), + require('karma-sourcemap-loader'), + require('karma-chai-sinon'), + require('karma-sinon'), + require('karma-webpack'), + require('karma-phantomjs-launcher') + ], - // list of files to exclude - exclude: gulpConfig.karma.exclude, + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + 'spec.bundle.js': ['webpack', 'sourcemap'] + }, - proxies: { - '/': 'http://localhost:8888/' + webpack: { + devtool: 'inline-source-map', + module: { + /*\ + preLoaders: [{ + test: /\.js$/, + loader: 'isparta' + }], + */ + loaders: [ + // transpile all files except modules and source files which are need be tested + { + test: /\.js$/, + exclude: [/app((?!\.spec).)*$/, /node_modules/], + loader: 'ng-annotate!babel' + }, + // transpile and instrument only testable files with isparta + // this allows us to generate a coverage report of all files + { + test: /\.js$/, + include: [/app((?!\.spec).)*$/], + loader: 'isparta' + }, { + test: /\.html$/, + loader: 'html' + }, { + test: /\.styl$/, + loader: 'style!css!stylus' + }, { + test: /\.less$/, + loader: 'css!less' + }, { + test: /\.css$/, + loader: 'style!css' + }, { + test: /\.(jpe?g|png|gif)$/i, + loader: 'file' + }, { + test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, + loader: 'url-loader?limit=10000&mimetype=application/font-woff' + }, { + test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, + loader: 'file' + } + ] + } }, - // preprocess matching files before serving them to the browser - // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor - preprocessors: gulpConfig.karma.preprocessors, + webpackServer: { + noInfo: true // prevent console spamming when running in Karma! + }, - // test results reporter to use - // possible values: 'dots', 'progress', 'coverage' // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ['progress', 'coverage', 'notify'], + reporters: ['mocha', 'coverage', 'notify'], coverageReporter: { - dir: gulpConfig.karma.coverage.dir, - reporters: gulpConfig.karma.coverage.reporters + // dir: report + 'coverage', + reporters: [ + // reporters not supporting the `file` property + { + type: 'html', + subdir: 'report-html' + }, { + type: 'lcov', + subdir: 'report-lcov' + }, { + type: 'text-summary' + } //, subdir: '.', file: 'text-summary.txt'} + ] }, - // web server port port: 9876, - // enable / disable colors in the output (reporters and logs) + // enable colors in the output colors: true, // level of logging - // possible values: config.LOG_DISABLE || config.LOG_ERROR || - // config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG logLevel: config.LOG_INFO, - // enable / disable watching file and executing tests whenever any file changes + // toggle whether to watch files and rerun tests upon incurring changes autoWatch: true, // start these browsers // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - // browsers: ['Chrome', 'ChromeCanary', 'FirefoxAurora', 'Safari', 'PhantomJS'], browsers: ['PhantomJS'], - // Continuous Integration mode - // if true, Karma captures browsers, runs the tests and exits + // if true, Karma runs tests once and exits singleRun: false }); }; diff --git a/app/templates/node-server/routes.js b/app/templates/node-server/routes.js index 11b1c5ed..102c67b3 100644 --- a/app/templates/node-server/routes.js +++ b/app/templates/node-server/routes.js @@ -13,7 +13,9 @@ var options = require('./utils/options')(); authHelper.init(); // [GJo] (#31) Moved bodyParsing inside routing, otherwise it might try to parse uploaded binaries as json.. -router.use(bodyParser.urlencoded({extended: true})); +router.use(bodyParser.urlencoded({ + extended: true +})); router.use(bodyParser.json()); router.get('/user/status', function(req, res) { @@ -23,8 +25,9 @@ router.get('/user/status', function(req, res) { if (options.guestAccess) { res.send(authStatus( true, - options.defaultUser, - { fullname: 'Guest' } + options.defaultUser, { + fullname: 'Guest' + } )); } else { res.send(authStatus( @@ -32,16 +35,24 @@ router.get('/user/status', function(req, res) { )); } } else { + var queryString = req.originalUrl.split('?')[1]; + var path = req.baseUrl + req.path + (queryString ? '?' + queryString : ''); + var passportUser = req.session.passport.user; + var reqOptions = { + hostname: options.mlHost, + port: options.mlHttpPort, + method: req.method, + path: path, + headers: req.headers + }; + delete headers['content-length']; - authHelper.getAuthorization(req.session, reqOptions.method, reqOptions.path, - { - authHost: reqOptions.hostname || options.mlHost, - authPort: reqOptions.port || options.mlHttpPort, - authUser: username, - authPassword: password - } - ).then(function(authorization) { - var passportUser = req.session.passport.user; + authHelper.getAuthorization(req.session, reqOptions.method, reqOptions.path, { + authHost: reqOptions.hostname || options.mlHost, + authPort: reqOptions.port || options.mlHttpPort, + authUser: passportUser.username, + authPassword: passportUser.password + }).then(function(authorization) { delete headers['content-length']; if (authorization) { headers.Authorization = authorization; @@ -118,8 +129,8 @@ router.get('/user/logout', function(req, res) { router.get('/*', four0four.notFoundMiddleware); function noCache(response) { - response.append('Cache-Control', 'no-cache, must-revalidate');//HTTP 1.1 - must-revalidate - response.append('Pragma', 'no-cache');//HTTP 1.0 + response.append('Cache-Control', 'no-cache, must-revalidate'); //HTTP 1.1 - must-revalidate + response.append('Pragma', 'no-cache'); //HTTP 1.0 response.append('Expires', 'Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past } diff --git a/app/templates/package.json b/app/templates/package.json index e1b29b15..a7b55480 100644 --- a/app/templates/package.json +++ b/app/templates/package.json @@ -2,82 +2,121 @@ "name": "@sample-app-name", "version": "0.0.0", "dependencies": { + "angular": "^1.5.0", + "angular-animate": "~1.5.x", + "angular-bootstrap-confirm": "^2.4.1", + "angular-cookies": "~1.5.x", + "angular-google-maps": "2.3.2", + "angular-highlightjs": "^0.6.1", + "angular-messages": "^1.5.8", + "angular-mocks": "~1.5.x", + "angular-ui-bootstrap": "^1.1", + "angular-ui-router": "^1.0.0-beta.1", + "angular-ui-tinymce": "~0.0.9", + "bardjs": "^0.1.8", "body-parser": "^1.14.0", + "bootstrap": "~3.3.5", "express": "^4.4.1", "express-session": "^1.5.0", - "helmet": "^2.0.0", + "font-awesome": "^4.6.3", + "helmet": "^2.1.1", + "highcharts": "^4.2", + "highlightjs": "~8.7.0", + "jquery": "~2.1.4", + "jshint": "^2.9.2", + "jshint-stylish": "^2.0.0", + "karma-chai-sinon": "^0.1.5", + "karma-sinon": "^1.0.5", + "less": "^2.7.1", + "lodash": "~3.10.1", + "ml-common-ng": "https://github.com/joemfb/ml-common-ng.git", + "ml-search-ng": "https://github.com/joemfb/ml-search-ng.git", + "ml-utils": "https://github.com/withjam/ml-utils.git", "morgan": "^1.6.0", + "ng-json-explorer": "odra/ng-json-explorer", + "ng-toast": "^2.0.0", + "normalize.css": "^3.0.3", "passport": "^0.3.2", "passport-local": "^1.0.0", "q": "^1.4.1", + "sinon": "^1.17.4", + "sinon-chai": "^2.8.0", + "tinymce": "4.3.12", "underscore": "^1.8.3", - "www-authenticate": "^0.6.2" + "vkbeautify": "*", + "www-authenticate": "^0.6.2", + "x2js": "https://github.com/Audarth/x2js.git#development" }, "devDependencies": { - "browser-sync": "^2.7.12", - "chai": "^3.0.0", - "del": "^2.0.2", - "glob": "^6.0.0", + "angular-mocks": "^1.5.0", + "babel-core": "^6.7.7", + "babel-loader": "^6.2.4", + "babel-plugin-transform-runtime": "^6.7.5", + "babel-polyfill": "^6.7.4", + "babel-preset-es2015": "^6.6.0", + "babel-preset-stage-0": "^6.5.0", + "babel-register": "^6.7.2", + "babel-runtime": "^6.6.1", + "browser-sync": "^2.11.1", + "chai": "^3.4.0", + "connect-history-api-fallback": "^1.1.0", + "css-loader": "^0.19.0", + "del": "^2.2.0", + "esprima": "^2.7.2", + "exports-loader": "^0.6.3", + "extract-text-webpack-plugin": "^1.0.1", + "file-loader": "^0.9.0", + "fs-walk": "0.0.1", "gulp": "^3.8.0", - "gulp-angular-templatecache": "^1.9.1", - "gulp-autoprefixer": "^2.2.0", "gulp-bump": "^1.0.0", - "gulp-bytediff": "^1.0.0", "gulp-concat": "^2.2.0", "postcss-svgo": "2.1.4", - "gulp-cssnano": "^2.0.0", - "gulp-filter": "^3.0.1", - "gulp-flatten": "0.2.0", "gulp-git": "^1.7.2", - "gulp-htmlmin": "^1.1.0", "gulp-if": "^2.0.0", - "gulp-imagemin": "^2.2.1", - "gulp-inject": "^3.0.0", "gulp-jscs": "^3.0.0", - "gulp-jshint": "^2.0.0", + "gulp-jshint": "^2.0.1", "gulp-json-editor": "^2.2.1", - "gulp-less": "^3.0.3", "gulp-load-plugins": "^1.0.0", - "gulp-ng-annotate": "^2.0.0", "gulp-nodemon": "^2.0.3", - "gulp-order": "^1.1.1", - "gulp-plumber": "^1.0.1", - "gulp-prompt": "^0.2.0", "gulp-print": "^2.0.1", - "gulp-rename": "^1.2.0", - "gulp-rev": "^7.0.0", - "gulp-rev-replace": "^0.4.2", - "gulp-sourcemaps": "^1.0.0", + "gulp-prompt": "^0.2.0", "gulp-task-listing": "^1.0.1", - "gulp-uglify": "^1.4.1", - "gulp-useref": "^3.0.0", - "gulp-util": "^3.0.5", - "inquirer": "^0.12.0", - "jasmine-core": "^2.3.4", - "jshint": "^2.0.0", - "jshint-stylish": "^2.0.0", - "karma": "^0.13.0", + "gulp-util": "^3.0.7", + "html-loader": "^0.4.3", + "html-webpack-plugin": "^2.24.1", + "image-webpack-loader": "~1.8.0", + "isparta-loader": "^2.0.0", + "karma": "^0.13.9", "karma-chai": "^0.1.0", - "karma-chai-sinon": "^0.1.5", - "karma-coverage": "^0.5.0", - "karma-jasmine": "^0.3.0", + "karma-chrome-launcher": "^0.2.0", + "karma-coverage": "^1.1.0", "karma-mocha": "^0.2.0", - "karma-ng-html2js-preprocessor": "^0.2.0", - "karma-notify-reporter": "^0.1.1", + "karma-mocha-reporter": "^1.0.2", + "karma-notify-reporter": "^1.0.1", "karma-phantomjs-launcher": "^1.0.0", - "karma-sinon": "^1.0.4", - "lodash": "^3.9.3", - "mocha": "^2.2.5", - "node-notifier": "^4.2.3", + "karma-sourcemap-loader": "^0.3.7", + "karma-webpack": "^1.8.0", + "less-loader": "^2.2.3", + "lodash": "^4.11.1", + "mocha": "^2.3.0", + "ng-annotate-loader": "0.0.10", + "node-libs-browser": "^0.5.0", "phantomjs-prebuilt": "^2.0.0", - "plato": "^1.5.0", - "q": "^1.0.0", - "sinon": "^1.15.3", - "sinon-chai": "^2.8.0", - "underscore.string": "^3.2.2", + "plato": "^1.6.0", + "raw-loader": "^0.5.1", + "run-sequence": "^1.1.0", + "script-loader": "^0.7.0", + "style-loader": "^0.12.2", + "stylus": "^0.54.5", + "stylus-loader": "^2.1.1", + "supports-color": "^3.1.2", + "url-loader": "^0.5.7", "username": "^1.0.1", + "webpack": "^1.9.5", + "webpack-dev-middleware": "^1.6.1", + "webpack-hot-middleware": "^2.6.0", "wiredep": "^3.0.0", - "yargs": "^3.26.0" + "yargs": "^3.9.0" }, "engines": { "node": ">=0.8.0" diff --git a/app/templates/spec.bundle.js b/app/templates/spec.bundle.js new file mode 100644 index 00000000..58ac6796 --- /dev/null +++ b/app/templates/spec.bundle.js @@ -0,0 +1,29 @@ +/* + * When testing with Webpack and ES6, we have to do some + * preliminary setup. Because we are writing our tests also in ES6, + * we must transpile those as well, which is handled inside + * `karma.conf.js` via the `karma-webpack` plugin. This is the entry + * file for the Webpack tests. Similarly to how Webpack creates a + * `bundle.js` file for the compressed app source files, when we + * run our tests, Webpack, likewise, compiles and bundles those tests here. + */ + +import angular from 'angular'; + +// Built by the core Angular team for mocking dependencies +import mocks from 'angular-mocks'; + +import 'script!bardjs/dist/bard'; + +// We use the context method on `require` which Webpack created +// in order to signify which files we actually want to require or import. +// Below, `context` will be a/an function/object with file names as keys. +// Using that regex, we scan within `client/app` and target +// all files ending with `.spec.js` and trace its path. +// By passing in true, we permit this process to occur recursively. +let context = require.context('./ui/app', true, /\.js/); + +// Get all files, for each file, call the context function +// that will require the file and load it here. Context will +// loop and require those spec files here. +context.keys().forEach(context); diff --git a/app/templates/ui/app/app.component.js b/app/templates/ui/app/app.component.js new file mode 100644 index 00000000..5ab7caee --- /dev/null +++ b/app/templates/ui/app/app.component.js @@ -0,0 +1,8 @@ +import template from './app.html'; + +let appComponent = { + template +}; + +export +default appComponent; diff --git a/app/templates/ui/app/app.html b/app/templates/ui/app/app.html new file mode 100644 index 00000000..f284be10 --- /dev/null +++ b/app/templates/ui/app/app.html @@ -0,0 +1,4 @@ + +