diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..f30d2772 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,79 @@ +module.exports = { + globals: { + 'window': true + }, + env: { + 'es6': true, // sets the "ecmaVersion" parser option to 6 + 'node': true + }, + extends: ['eslint:recommended'], + rules: { + 'comma-dangle': 2, + 'no-console': 0, + 'no-control-regex': 0, + 'no-debugger': 1, + 'no-empty': 0, + 'no-negated-in-lhs': 2, + 'no-regex-spaces': 0, + 'no-unexpected-multiline': 0, + 'block-scoped-var': 1, + 'dot-location': [1, 'property'], + 'eqeqeq': [1, 'smart'], + 'no-caller': 2, + 'no-div-regex': 1, + 'no-eval': 1, + 'no-extra-bind': 1, + 'no-floating-decimal': 1, + 'no-implied-eval': 1, + 'no-iterator': 2, + 'no-labels': 2, + 'no-native-reassign': 2, + 'no-new-func': 2, + 'no-new-wrappers': 2, + 'no-new': 1, + 'no-octal-escape': 1, + 'no-proto': 2, + 'no-redeclare': [2, {'builtinGlobals': true}], + 'no-return-assign': [2, 'except-parens'], + 'no-self-compare': 2, + 'no-sequences': 2, + 'no-throw-literal': 2, + 'no-unused-expressions': [1, {'allowShortCircuit': true, 'allowTernary': true}], + 'no-useless-call': 2, + 'no-useless-return': 1, + 'no-with': 2, + 'radix': [1, 'as-needed'], + 'wrap-iife': [2, 'inside'], + 'no-catch-shadow': 2, + 'no-label-var': 2, + 'no-shadow-restricted-names': 2, + 'no-shadow': [2, {'builtinGlobals': true, 'hoist': 'all', 'allow': ['context']}], + 'no-use-before-define': [2, {'functions': false}], + 'array-bracket-spacing': 1, + 'brace-style': [1, '1tbs', {'allowSingleLine': true }], + 'comma-spacing': 1, + 'comma-style': 1, + 'computed-property-spacing': 1, + 'eol-last': 1, + 'indent': [1, 'tab', {'SwitchCase': 1}], + 'jsx-quotes': 1, + 'linebreak-style': 1, + 'new-cap': [2, {'newIsCap': true, 'capIsNew': false}], + 'new-parens': 1, + 'no-array-constructor': 2, + 'no-lonely-if': 1, + 'no-mixed-spaces-and-tabs': 1, + 'no-new-object': 1, + 'func-call-spacing': 1, + 'no-trailing-spaces': 1, + 'no-unneeded-ternary': 1, + 'operator-linebreak': [1, 'before'], + 'quotes': [1, 'single', {'avoidEscape':true}], + 'space-before-function-paren': [1, 'never'], + 'space-infix-ops': 0, + 'space-unary-ops': [1, {'words': true, 'nonwords': false}], + 'spaced-comment': [1, 'always', {'markers': ['*']}], + 'arrow-spacing': 1, + 'require-yield': 0 + } +}; diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..6a7b73af --- /dev/null +++ b/.travis.yml @@ -0,0 +1,9 @@ +language: node_js +node_js: + - "node" +sudo: false +install: + - npm install + +script: + - npm run lint diff --git a/CHANGELOG.md b/CHANGELOG.md index 84b2ad29..09a636bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +## 0.7.0 (March 31, 2017) + +Added support for a link command (`enact link`) as a shorthand to link in Enact library dependencies. + +### pack + +* Added support for appinfo.json sysAssetsBasePath and $-prefix in webos-meta-webpack-plugin. +* Will now warn about performance when building in development mode. +* HTML template will now be used in all situations and can be customized as desired. +* Vastly rewritten isomorphic app prerendering support with improved reliability and mmemory management. +* Depreciated prerendering of isomorphic apps within the HTML template has been removed. Please ensure all app entrypoints are able to self-render. See [this example](https://github.com/enyojs/enact-dev/blob/master/template/src/index.js). + +### test + +* Improved error handling with plugins to prevent certain scenarios that could cause tests to fail. + +### lint + +* Updated lint rules for disabling `no-spaced-func` and `no-undefined` warnings for non-strict ruleset. + ## 0.6.0 (February 22, 2017) All enact-dev dependencies have been updated to latest applicable revisions. If you are using editor-based linting, please update your global dependencies to match. diff --git a/LICENSE b/LICENSE index e0fadba2..86abe9dc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,59 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +BSD License - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +For enact-dev software: - 1. Definitions. +Copyright (c) 2016-present, LG Electronics All rights reserved. - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. + * Neither the name LG Electronics nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. +For create-react-app software: - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). +Copyright (c) 2016-present, Facebook, Inc. All rights reserved. - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright2016 LG Silicon Valley Lab - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/PATENTS b/PATENTS new file mode 100644 index 00000000..ca1e727b --- /dev/null +++ b/PATENTS @@ -0,0 +1,33 @@ +Additional Grant of Patent Rights Version 2 + +"Software" means the create-react-app software distributed by Facebook, Inc. + +Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software +("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable +(subject to the termination provision below) license under any Necessary +Claims, to make, have made, use, sell, offer to sell, import, and otherwise +transfer the Software. For avoidance of doubt, no license is granted under +Facebook’s rights in any patent claims that are infringed by (i) modifications +to the Software made by you or any third party or (ii) the Software in +combination with any software or other technology. + +The license granted hereunder will terminate, automatically and without notice, +if you (or any of your subsidiaries, corporate affiliates or agents) initiate +directly or indirectly, or take a direct financial interest in, any Patent +Assertion: (i) against Facebook or any of its subsidiaries or corporate +affiliates, (ii) against any party if such Patent Assertion arises in whole or +in part from any software, technology, product or service of Facebook or any of +its subsidiaries or corporate affiliates, or (iii) against any party relating +to the Software. Notwithstanding the foregoing, if Facebook or any of its +subsidiaries or corporate affiliates files a lawsuit alleging patent +infringement against you in the first instance, and you respond by filing a +patent infringement counterclaim in that lawsuit against that party that is +unrelated to the Software, the license granted hereunder will not terminate +under section (i) of this paragraph due to such counterclaim. + +A "Necessary Claim" is a claim of a patent owned by Facebook that is +necessarily infringed by the Software standing alone. + +A "Patent Assertion" is any lawsuit or other action alleging direct, indirect, +or contributory infringement or inducement to infringe any patent, including a +cross-claim or counterclaim. diff --git a/README.md b/README.md index 432fd0d4..35f2fbed 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A standalone dev environment for Enact apps using Webpack, Babel, React, and a c ## Installation All that's needed to install enact-dev is to use npm to install it globally. For Linux `sudo` may be required. ``` -npm install -g enyojs/enact-dev +npm install -g enact-dev ``` >Note: Node 6.x+ is highly recommended for optimum speed and efficiency, however anything since Node 4.x is compatible. @@ -18,7 +18,7 @@ enact create [directory] This will generate a basic App template, complete with npm scripts and dependencies setup. If no directory path is specified, it will be generated within the working directory. ->Advanced: If you've used `npm link` on separate installations of the Enact repo, you can include `--link` to the `init` command and NPM will symlink your Enact repo, rather than reinstall. +>Advanced: If you've used `npm link` on separate installations of the Enact repo, you can include `--link` to the `init` command and NPM will symlink your Enact repo, rather than reinstall. Additionally, you can link in the Enact dependencies post-install via the `enact link` command. ## Available App Scripts @@ -33,7 +33,7 @@ The page will reload if you make edits.
### `npm run pack` and `npm run pack-p` -Builds the project in the working directory. Specifically, `pack` builds in development mode with code un-minified and with debug code include, whereas `pack-p` builds in production mode, with everything minified and optimized for performance. +Builds the project in the working directory. Specifically, `pack` builds in development mode with code un-minified and with debug code included, whereas `pack-p` builds in production mode, with everything minified and optimized for performance. Be sure to avoid shipping or performance testing on development mode builds. ### `npm run watch` @@ -99,3 +99,17 @@ npm install -g eslint eslint-plugin-react eslint-plugin-babel babel-eslint enyoj ``` We recognize that this is suboptimal, but it is currently required due to the way we hide the ESLint dependency. The ESLint team is already [working on a solution to this](https://github.com/eslint/eslint/issues/3458) so this may become unnecessary in a couple of months. + +## Copyright and License Information + +Unless otherwise specified, all content, including all source code files and documentation files in this repository are: + +Copyright (c) 2012-2017 LG Electronics + +Unless otherwise specified or set forth in the NOTICE file, all content, including all source code files and documentation files in this repository are: Licensed under the BSD License (the "License"); you may not use this content except in compliance with the License. You may obtain a copy of the License at + +https://opensource.org/licenses/BSD-3-Clause + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +Portions of this project are based upon [create-react-app](https://github.com/facebookincubator/create-react-app), Copyright (C) 2016-present Facebook, Inc. diff --git a/bin/enact.js b/bin/enact.js index a3778152..a455fb54 100755 --- a/bin/enact.js +++ b/bin/enact.js @@ -2,7 +2,7 @@ 'use strict'; -if(process.argv.indexOf('-v')>=0 || process.argv.indexOf('--version')>=0) { +if (process.argv.indexOf('-v') >= 0 || process.argv.indexOf('--version') >= 0) { var pkg = require('../package.json'); console.log(pkg.name); console.log('version: ' + pkg.version); @@ -10,13 +10,9 @@ if(process.argv.indexOf('-v')>=0 || process.argv.indexOf('--version')>=0) { } else { var command = process.argv[2]; - switch(command) { - case 'init': - var chalk = require('chalk'); - console.log(chalk.gray('Warning: \'enact init\' is depreciated.' - + ' Please use \'enact create\'')); - command = 'create'; + switch (command) { case 'create': + case 'link': case 'serve': case 'transpile': case 'pack': diff --git a/config/polyfills.js b/config/polyfills.js index f8f9cfec..bdac7db1 100644 --- a/config/polyfills.js +++ b/config/polyfills.js @@ -1,4 +1,5 @@ - // @remove-on-eject-begin +/* global global */ +// @remove-on-eject-begin /** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. @@ -9,13 +10,13 @@ */ // @remove-on-eject-end -if (typeof window === 'object') { - if (typeof Promise === 'undefined' && typeof window === 'object') { +if (typeof global !== 'undefined') { + if (typeof Promise === 'undefined') { // Rejection tracking prevents a common issue where React gets into an // inconsistent state due to an error, but it gets swallowed by a Promise, // and the user has no idea what causes React's erratic future behavior. require('promise/lib/rejection-tracking').enable(); - window.Promise = require('promise/lib/es6-extensions'); + global.Promise = require('promise/lib/es6-extensions'); } // fetch() polyfill for making API calls. @@ -24,9 +25,9 @@ if (typeof window === 'object') { if (!Math.sign) { Math.sign = function(x) { - // If -0, must return -0. - return isNaN(x) ? NaN : x < 0 ? -1 : x > 0 ? 1 : +x; - } + // If -0, must return -0. + return isNaN(x) ? NaN : x < 0 ? -1 : x > 0 ? 1 : +x; + } } // Common String ES6 functionalities for character values. diff --git a/config/proptype-checker.js b/config/proptype-checker.js index b3078cdc..c79b5c72 100644 --- a/config/proptype-checker.js +++ b/config/proptype-checker.js @@ -1,7 +1,7 @@ -/* global console */ +/* global console, beforeEach, afterEach, expect */ /* eslint no-var: off, no-console: ["error", { allow: ["error"] }] */ -var spy = require('enyo-console-spy'); +var spy = require('console-snoop'); var watchErrorAndWarnings = spy.watchErrorAndWarnings; var filterErrorAndWarnings = spy.filterErrorAndWarnings; @@ -9,7 +9,7 @@ var restoreErrorAndWarnings = spy.restoreErrorAndWarnings; beforeEach(watchErrorAndWarnings); -afterEach(function (done) { +afterEach(function(done) { const actual = filterErrorAndWarnings(/(Invalid prop|Failed prop type|Unknown prop)/); const expected = 0; restoreErrorAndWarnings(); diff --git a/config/webpack.config.dev.js b/config/webpack.config.dev.js index 0017a871..8495f5b6 100644 --- a/config/webpack.config.dev.js +++ b/config/webpack.config.dev.js @@ -201,9 +201,9 @@ module.exports = { '>1%', 'last 4 versions', 'Firefox ESR', - 'not ie < 9', // React doesn't support IE8 anyway + 'not ie < 9' // React doesn't support IE8 anyway ] - }), + }) ]; }, // Options for the LESS loader diff --git a/config/webpack.config.prod.js b/config/webpack.config.prod.js index 1264ccf4..d3e54c0e 100644 --- a/config/webpack.config.prod.js +++ b/config/webpack.config.prod.js @@ -101,7 +101,7 @@ module.exports = { query: { babelrc: false, extends: path.join(__dirname, '.babelrc') - }, + } // @remove-on-eject-end }, // Multiple styling-support features are used together. @@ -127,7 +127,7 @@ module.exports = { // Webpack 1.x uses Uglify plugin as a signal to minify *all* the assets // including CSS. This is confusing and will be removed in Webpack 2: // https://github.com/webpack/webpack/issues/283 - loader: ExtractTextPlugin.extract('style', + loader: ExtractTextPlugin.extract('style', 'css?-autoprefixer&modules&importLoaders=1!postcss!less') // Note: this won't work without `new ExtractTextPlugin()` in `plugins`. }, @@ -174,7 +174,7 @@ module.exports = { '>1%', 'last 4 versions', 'Firefox ESR', - 'not ie < 9', // React doesn't support IE8 anyway + 'not ie < 9' // React doesn't support IE8 anyway ] }), // Remove the development-only CSS class .__DEV__ diff --git a/global-cli/create.js b/global-cli/create.js index 8ef85d7a..86903cc5 100755 --- a/global-cli/create.js +++ b/global-cli/create.js @@ -7,33 +7,32 @@ var semver = require('semver'), exists = require('path-exists').sync; -// @TODO: switch back to master when 0.2.0 releases var ENACT_DEV_NPM = 'enyojs/enact-dev'; - + function createApp(output, template, link, local, verbose) { - var root = path.resolve(output); - var appName = path.basename(root); + var project = path.resolve(output); + var appName = path.basename(project); checkNodeVersion(); checkAppName(appName, template); - if (!exists(root)) { - fs.mkdirSync(root); - } else if (!isSafeToCreateProjectIn(root)) { + if (!exists(project)) { + fs.mkdirSync(project); + } else if (!isSafeToCreateProjectIn(project)) { console.log('The directory "' + output + '" contains file(s) that could conflict. Aborting.'); process.exit(1); } - console.log('Creating a new Enact app in ' + root + '.'); + console.log('Creating a new Enact app in ' + project + '.'); console.log(); var prevCWD = process.cwd(); - process.chdir(root); - copyTemplate(template, root); + process.chdir(project); + copyTemplate(template, project); - installDeps(root, link, local, verbose, function() { + installDeps(project, link, local, verbose, function() { console.log(); - console.log('Success! Created ' + appName + ' at ' + root); + console.log('Success! Created ' + appName + ' at ' + project); console.log(); console.log('Inside that directory, you can run several NPM commands, including:'); console.log(chalk.cyan(' npm run serve')); @@ -46,16 +45,16 @@ function createApp(output, template, link, local, verbose) { console.log(' Starts the test runner.'); console.log(); // @TODO - //console.log(chalk.cyan(' npm run eject')); - //console.log(' Removes this tool and copies build dependencies, configuration files'); - //console.log(' and scripts into the app directory. If you do this, you can’t go back!'); - //console.log(); + // console.log(chalk.cyan(' npm run eject')); + // console.log(' Removes this tool and copies build dependencies, configuration files'); + // console.log(' and scripts into the app directory. If you do this, you can’t go back!'); + // console.log(); console.log('We suggest that you begin by typing:'); - if(prevCWD!=process.cwd()) { + if(prevCWD!==process.cwd()) { console.log(chalk.cyan(' cd'), output); } console.log(' ' + chalk.cyan('npm run serve')); - if(exists(path.join(root, 'README.old.md'))) { + if(exists(path.join(project, 'README.old.md'))) { console.log(); console.log(chalk.yellow('You had a `README.md` file, we renamed it to `README.old.md`')); } @@ -74,7 +73,7 @@ function copyTemplate(template, dest) { // Rename gitignore after the fact to prevent npm from renaming it to .npmignore // See: https://github.com/npm/npm/issues/1862 - fs.move(path.join(dest, 'gitignore'), path.join(dest, '.gitignore'), [], function (err) { + fs.move(path.join(dest, 'gitignore'), path.join(dest, '.gitignore'), [], function(err) { if (err) { // Append if there's already a `.gitignore` file there if (err.code === 'EEXIST') { @@ -108,7 +107,7 @@ function copyTemplate(template, dest) { } } -function installDeps(root, link, local, verbose, callback) { +function installDeps(project, link, local, verbose, callback) { var args = [ '--loglevel', (verbose ? 'verbose' : 'error'), @@ -118,7 +117,7 @@ function installDeps(root, link, local, verbose, callback) { console.log('Installing dependencies from npm...'); - var proc = spawn('npm', args, {stdio: 'inherit', cwd:root}); + var proc = spawn('npm', args, {stdio: 'inherit', cwd:project}); proc.on('close', function(code) { if(code!==0) { console.log(chalk.cyan('ERROR: ') + '"npm ' + args.join(' ') + '" failed'); @@ -134,9 +133,9 @@ function installDeps(root, link, local, verbose, callback) { ENACT_DEV_NPM, '--save-dev' ].filter(function(e) { return e; }); - var devProc = spawn('npm', devArgs, {stdio: 'inherit', cwd:root}); - devProc.on('close', function(code) { - if(code!==0) { + var devProc = spawn('npm', devArgs, {stdio: 'inherit', cwd:project}); + devProc.on('close', function(code2) { + if(code2!==0) { console.log(chalk.cyan('ERROR: ') + '"npm ' + devArgs.join(' ') + '" failed'); process.exit(1); } @@ -160,14 +159,9 @@ function checkNodeVersion() { } if (!semver.satisfies(process.version, packageJson.engines.node)) { - console.error( - chalk.red( - 'You are currently running Node %s but enact-dev requires %s.' + - ' Please use a supported version of Node.\n' - ), - process.version, - packageJson.engines.node - ); + console.error(chalk.red('You are currently running Node %s but enact-dev requires %s.' + + ' Please use a supported version of Node.\n'), process.version, + packageJson.engines.node); process.exit(1); } } @@ -179,28 +173,24 @@ function checkAppName(appName, template) { var allDependencies = dependencies.concat(devDependencies).sort(); if (allDependencies.indexOf(appName) >= 0) { - console.error( - chalk.red( - 'We cannot create a project called `' + appName + '` because a dependency with the same name exists.\n' + - 'Due to the way npm works, the following names are not allowed:\n\n' - ) + - chalk.cyan( - allDependencies.map(function(depName) { + console.error(chalk.red('We cannot create a project called `' + appName + + '` because a dependency with the same name exists.\n' + + 'Due to the way npm works, the following names are not allowed:\n\n') + + chalk.cyan(allDependencies.map(function(depName) { return ' ' + depName; - }).join('\n') - ) + - chalk.red('\n\nPlease choose a different project name.') + }).join('\n')) + + chalk.red('\n\nPlease choose a different project name.') ); process.exit(1); } } // If project only contains files generated by GH, it’s safe. -function isSafeToCreateProjectIn(root) { +function isSafeToCreateProjectIn(project) { var validFiles = [ '.DS_Store', 'Thumbs.db', '.git', '.gitignore', '.idea', 'README.md', 'LICENSE' ]; - return fs.readdirSync(root) + return fs.readdirSync(project) .every(function(file) { return validFiles.indexOf(file) >= 0; }); diff --git a/global-cli/link.js b/global-cli/link.js new file mode 100755 index 00000000..328ce0dc --- /dev/null +++ b/global-cli/link.js @@ -0,0 +1,68 @@ +var + spawn = require('cross-spawn'), + path = require('path'), + dir = require('global-modules'), + chalk = require('chalk'), + exists = require('path-exists').sync, + minimist = require('minimist'); + +var enact = [ + 'core', + 'ui', + 'moonstone', + 'spotlight', + 'i18n', + 'webos' +]; + +function displayHelp() { + console.log(' Usage'); + console.log(' enact link [options]'); + console.log(); + console.log(' Options'); + console.log(' -verbose Verbose output logging'); + console.log(' -v, --version Display version information'); + console.log(' -h, --help Display help information'); + console.log(); + process.exit(0); +} + +module.exports = function(args) { + var opts = minimist(args, { + boolean: ['verbose', 'h', 'help'], + alias: {h:'help'} + }); + opts.help && displayHelp(); + + var linkArgs = [ + '--loglevel', + (opts.verbose ? 'verbose' : 'error'), + 'link' + ]; + + var missing = []; + for(var i=0; i=0 ? babel : 0), 0, { + test: fs.realpathSync(locale), + loader: 'expose?iLibLocale' + }); + } + } } // If 'isomorphic' value is a string, use custom entrypoint. @@ -49,18 +67,19 @@ module.exports = function(config, opts) { // Use universal module definition to allow usage in Node and browser environments. config.output.libraryTarget = 'umd'; - // Update HTML webpack plugin to use the isomorphic template and include screentypes - var htmlPlugin = helper.getPluginByName(config, 'HtmlWebpackPlugin'); - if(htmlPlugin) { - htmlPlugin.options.inject = false; - htmlPlugin.options.template = path.join(__dirname, 'util', 'html-template-isomorphic.ejs'); - htmlPlugin.options.screenTypes = enact.screenTypes + // Include plugin to prerender the html into the index.html + var prerenderOpts = { + server: require(reactDOMServer), + locales: opts.locales, + externals: opts.externals, + screenTypes: enact.screenTypes || readJSON('./node_modules/@enact/moonstone/MoonstoneDecorator/screenTypes.json') - || readJSON('./node_modules/enact/packages/moonstone/MoonstoneDecorator/screenTypes.json'); } - - // Include plugin to prerender the html into the index.html - config.plugins.push(new PrerenderPlugin()); + if(!opts.locales) { + config.plugins.push(new PrerenderPlugin(prerenderOpts)); + } else { + config.plugins.push(new LocaleHtmlPlugin(prerenderOpts)); + } // Apply snapshot specialization options if needed if(opts.snapshot && !opts.externals) { diff --git a/global-cli/modifiers/snapshot.js b/global-cli/modifiers/snapshot.js index 3bd3bd4f..c530b340 100644 --- a/global-cli/modifiers/snapshot.js +++ b/global-cli/modifiers/snapshot.js @@ -1,6 +1,5 @@ var path = require('path'), - fs = require('fs'), exists = require('path-exists').sync, helper = require('./util/config-helper'), SnapshotPlugin = require('./util/SnapshotPlugin'); @@ -20,19 +19,12 @@ module.exports = function(config, opts) { } // Snapshot helper API for the transition from v8 snapshot into the window config.entry.main.splice(-1, 0, require.resolve('./util/snapshot-helper')); - - // Expose iLib locale utility function module so we can update the locale on page load, if used - var babel = helper.findLoader(config, 'babel'); - config.module.loaders.splice((babel>=0 ? babel : 0), 0, { - test: fs.realpathSync(path.join(process.cwd(), 'node_modules', '@enact', 'i18n', 'src', 'locale.js')), - loader: 'expose?iLibLocale' - }); } // Include plugin to attempt generation of v8 snapshot binary if V8_MKSNAPSHOT env var is set config.plugins.push(new SnapshotPlugin({ target: (opts.framework ? 'enact.js' : 'main.js') // Disabled temporarily until effectiveness is proven - //append: (opts.framework ? '\nenact_framework.load();\n' : undefined) + // append: (opts.framework ? '\nenact_framework.load();\n' : undefined) })); }; diff --git a/global-cli/modifiers/stats.js b/global-cli/modifiers/stats.js index 6df78651..e34b157d 100644 --- a/global-cli/modifiers/stats.js +++ b/global-cli/modifiers/stats.js @@ -1,6 +1,6 @@ var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; -module.exports = function(config, opts) { +module.exports = function(config) { config.plugins.push(new BundleAnalyzerPlugin({ analyzerMode: 'static', reportFilename: 'stats.html', diff --git a/global-cli/modifiers/unmangled.js b/global-cli/modifiers/unmangled.js index 3d4634df..a83930f2 100644 --- a/global-cli/modifiers/unmangled.js +++ b/global-cli/modifiers/unmangled.js @@ -1,6 +1,6 @@ var helper = require('./util/config-helper'); -module.exports = function(config, opts) { +module.exports = function(config) { // Allow Uglify's optimizations/debug-code-removal but don't minify var uglifyPlugin = helper.getPluginByName(config, 'UglifyJsPlugin'); if(uglifyPlugin) { diff --git a/global-cli/modifiers/util/EnactFrameworkPlugin.js b/global-cli/modifiers/util/EnactFrameworkPlugin.js index 3ee5e74c..894a65ca 100644 --- a/global-cli/modifiers/util/EnactFrameworkPlugin.js +++ b/global-cli/modifiers/util/EnactFrameworkPlugin.js @@ -3,7 +3,7 @@ var fs = require('fs'), DllEntryPlugin = require('webpack/lib/DllEntryPlugin'), DllModule = require('webpack/lib/DllModule'), - RawSource = require("webpack/lib/RawSource"), + RawSource = require('webpack/lib/RawSource'), exists = require('path-exists').sync; var pkgCache = {}; @@ -29,12 +29,10 @@ var findParent = function(dir) { var currPkg = path.join(dir, 'package.json'); if(exists(currPkg)) { return dir; + } else if(dir === '/' || dir === '' || dir === '.') { + return null; } else { - if(dir === '/' || dir === '' || dir === '.') { - return null; - } else { - return findParent(path.dirname(dir)); - } + return findParent(path.dirname(dir)); } } }; @@ -113,12 +111,12 @@ EnactFrameworkPlugin.prototype.apply = function(compiler) { // Format the internal module ID to a usable named descriptor compiler.plugin('compilation', function(compilation) { compilation.plugin('before-module-ids', function(modules) { - modules.forEach(function(module) { - if(module.id === null && module.libIdent) { - module.id = module.libIdent({ + modules.forEach(function(m) { + if(m.id === null && m.libIdent) { + m.id = m.libIdent({ context: this.options.context || compiler.options.context }); - module.id = normalizeModuleID(module.id) + m.id = normalizeModuleID(m.id) } }, this); }.bind(this)); diff --git a/global-cli/modifiers/util/EnactFrameworkRefPlugin.js b/global-cli/modifiers/util/EnactFrameworkRefPlugin.js index 540fd5f8..5783f8e2 100644 --- a/global-cli/modifiers/util/EnactFrameworkRefPlugin.js +++ b/global-cli/modifiers/util/EnactFrameworkRefPlugin.js @@ -34,14 +34,12 @@ function normalizePath(dir, file, compiler) { } } +// Determine if it's a NodeJS output filesystem or if it's a foreign/virtual one. function isNodeOutputFS(compiler) { - try { - var NodeOutputFileSystem = require('webpack/lib/node/NodeOutputFileSystem'); - return (compiler.outputFileSystem.writeFile===NodeOutputFileSystem.prototype.writeFile); - } catch(e) { - console.error('SnapshotPlugin loader is not compatible with standalone global installs of Webpack.'); - return false; - } + return (compiler.outputFileSystem + && compiler.outputFileSystem.constructor + && compiler.outputFileSystem.constructor.name + && compiler.outputFileSystem.constructor.name === 'NodeOutputFileSystem'); } function updateAppInfo(output, blob) { @@ -66,9 +64,9 @@ function EnactFrameworkRefPlugin(opts) { this.options.external = this.options.external || {}; this.options.external.inject = this.options.external.inject || this.options.external.path; - if(!process.env.ILIB_LOCALE_PATH) { - process.env.ILIB_LOCALE_PATH = path.join(this.options.external.inject, 'node_module', - '@enact', 'i18n', 'ilib', 'locale'); + if(!process.env.ILIB_BASE_PATH) { + process.env.ILIB_BASE_PATH = path.join(this.options.external.inject, 'node_module', + '@enact', 'i18n', 'ilib'); } } module.exports = EnactFrameworkRefPlugin; @@ -85,9 +83,9 @@ EnactFrameworkRefPlugin.prototype.apply = function(compiler) { compiler.plugin('compilation', function(compilation, params) { var normalModuleFactory = params.normalModuleFactory; compilation.dependencyFactories.set(DelegatedSourceDependency, normalModuleFactory); - - compilation.plugin('html-webpack-plugin-alter-chunks', function(chunks, params) { - var chunkFiles = [ normalizePath(external.inject, 'enact.css', compiler) ]; + + compilation.plugin('html-webpack-plugin-alter-chunks', function(chunks) { + var chunkFiles = [normalizePath(external.inject, 'enact.css', compiler)]; if(!external.snapshot) { chunkFiles.unshift(normalizePath(external.inject, 'enact.js', compiler)); } @@ -96,18 +94,22 @@ EnactFrameworkRefPlugin.prototype.apply = function(compiler) { names: ['enact_framework'], files: chunkFiles }); - - // Store the absolute filepath to the external framework so the PrerenderPlugin can use it - params.plugin.options.externalFramework = path.resolve(path.join(external.path, 'enact.js')); return chunks; }); + + if(external.snapshot && isNodeOutputFS(compiler)) { + compilation.plugin('webos-meta-root-appinfo', function(meta) { + meta.v8SnapshotFile = normalizePath(external.inject, 'snapshot_blob.bin', compiler); + return meta; + }); + } }); // Apply the Enact factory plugin to handle the require() delagation/rerouting compiler.plugin('compile', function(params) { params.normalModuleFactory.apply(new DelegatedEnactFactoryPlugin({ name: name, - libraries: libs, + libraries: libs })); }); diff --git a/global-cli/modifiers/util/FileXHR.js b/global-cli/modifiers/util/FileXHR.js new file mode 100644 index 00000000..4c6c0fc5 --- /dev/null +++ b/global-cli/modifiers/util/FileXHR.js @@ -0,0 +1,35 @@ +var + fs = require('fs'), + exists = require('path-exists').sync; + +function FileXHR() {} + +FileXHR.prototype.open = function(method, uri, async) { + this.method = method; + this.uri = uri; + this.sync = (async === false); +}; + +FileXHR.prototype.addEventListener = function(evt, fn) { + this['on' + evt] = fn; +}; + +FileXHR.prototype.send = function() { + if(this.method.toUpperCase() === 'GET' && this.uri && this.sync) { + var parsedURI = this.uri.replace(/\\/g, '/').replace(/^(_\/)+/g, function(match) { + return match.replace(/_/g, '..'); + }); + try { + if(!exists(parsedURI)) throw new Error('File not found: ' + this.uri); + + this.response = this.responseText = fs.readFileSync(parsedURI, {encoding:'utf8'}); + this.status = 200; + this.onload && this.onload(); + } catch(e) { + this.status = 404; + this.onerror && this.onerror(e.message || e); + } + } +}; + +module.exports = FileXHR; diff --git a/global-cli/modifiers/util/LocaleHtmlPlugin.js b/global-cli/modifiers/util/LocaleHtmlPlugin.js new file mode 100644 index 00000000..e2255c8f --- /dev/null +++ b/global-cli/modifiers/util/LocaleHtmlPlugin.js @@ -0,0 +1,210 @@ +var + path = require('path'), + fs = require('fs'), + vdomRender = require('./vdom-server-render'); + +// Determine if it's a NodeJS output filesystem or if it's a foreign/virtual one. +function isNodeOutputFS(compiler) { + return (compiler.outputFileSystem + && compiler.outputFileSystem.constructor + && compiler.outputFileSystem.constructor.name + && compiler.outputFileSystem.constructor.name === 'NodeOutputFileSystem'); +} + +// Determine the desired target locales based of option content. +// Can be a preset like 'tv' or 'signage', 'used' for all used app-level locales, 'all' for +// all locales supported by ilib, a custom json file input, or a comma-separated lists +function parseLocales(context, target) { + if(!target || target === 'none') { + return []; + } else if(Array.isArray(target)) { + return target; + } else if(target === 'tv') { + return JSON.parse(fs.readFileSync(path.join(__dirname, 'locales-tv.json'), {encoding: 'utf8'})).paths; + } else if(target === 'signage') { + return JSON.parse(fs.readFileSync(path.join(__dirname, 'locales-signage.json'), {encoding: 'utf8'})).paths; + } else if(target === 'used') { + return localesInManifest(path.join(context, 'resources', 'ilibmanifest.json')); + } else if(target === 'all') { + return localesInManifest('node_modules/@enact/i18n/ilibmanifest'); + } else if(/\.json$/i.test(target)) { + return JSON.parse(fs.readFileSync(target, {encoding: 'utf8'})).paths; + } else { + return target.replace(/-/g, '/').split(','); + } +} + +// Find the location of the root div (can be empty or with contents) and return the +// contents of the HTML before and after it. +function findRootDiv(html, start, end) { + if(/^]+id="root"/i.test(html.substring(start, end+7))) { + return {before:html.substring(0, start), after:html.substring(end+6)}; + } + var a = html.indexOf('', end); + if(a>=0 && b>=0 && a b.split('/').length; + }); + return locales; + } catch(e) { + return []; + } +} + +// Add a localized index.html to the compilation assets. +function localizedHtmlAsset(compilation, locale, data) { + compilation.assets['index.' + locale.replace(/[\\\/]/g, '-') + '.html'] = { + size: function() { return data.length; }, + source: function() { return data; }, + updateHash: function(hash) { return hash.update(data); }, + map: function() { return null; } + }; +} + +function LocaleHtmlPlugin(options) { + this.options = options || {}; + this.options.chunk = this.options.chunk || 'main.js'; + if(typeof this.options.locales === 'undefined') { + this.options.locales = 'used'; + } +} + +LocaleHtmlPlugin.prototype.apply = function(compiler) { + var opts = this.options; + var status = {prerender:{}, failed:[], err:{}}; + var jsAssets = []; + + // Determine the target locales and load up the startup scripts. + var locales = parseLocales(compiler.options.context, opts.locales); + + // Prerender each locale desired and output an error on failure. + compiler.plugin('compilation', function(compilation) { + if(isNodeOutputFS(compiler)) { + compilation.plugin('chunk-asset', function(chunk, file) { + if(file === opts.chunk) { + compilation.applyPlugins('prerender-chunk', {chunk:opts.chunk, locales:locales}); + var src = compilation.assets[opts.chunk].source(), locStr; + for(var i=0; i=0 && !status.err[locCode]) { + meta.main = 'index.' + info.locale.replace(/[\\\/]/g, '-') + '.html'; + meta.usePrerendering = true; + } + return meta; + }); + + // Force HtmlWebpackPlugin to use body inject format and set aside the js assets. + compilation.plugin('html-webpack-plugin-before-html-processing', function(htmlPluginData, callback) { + htmlPluginData.plugin.options.inject = 'body'; + jsAssets = htmlPluginData.assets.js; + htmlPluginData.assets.js = []; + callback(null, htmlPluginData); + }); + + // Use the prerendered-startup.js to asynchronously add the js assets at load time and embed that + // script inline in the HTML head. + compilation.plugin('html-webpack-plugin-alter-asset-tags', function(htmlPluginData, callback) { + var startup = fs.readFileSync(path.join(__dirname, 'prerendered-startup.txt'), {encoding:'utf8'}); + startup = startup.replace('%SCREENTYPES%', JSON.stringify(opts.screenTypes)) + .replace('%JSASSETS%', JSON.stringify(jsAssets)); + htmlPluginData.head.unshift({ + tagName: 'script', + closeTag: true, + attributes: { + type: 'text/javascript' + }, + innerHTML: startup + }); + callback(null, htmlPluginData); + }); + + // Generate an isomorphic HTML template and insert the prerendered locales with it into locale-specific + // index.html files. Afterward, generate and updated root HTML template for fallback. + compilation.plugin('html-webpack-plugin-after-html-processing', function(htmlPluginData, callback) { + var tokens = findRootDiv(htmlPluginData.html, 0, htmlPluginData.html.length-6); + if(tokens) { + compilation.applyPlugins('locale-html-generate', {chunk:opts.chunk, locales:locales}); + for(var i=0; i' + + status.prerender[locales[i]] + '' + tokens.after); + } + } + callback(null, htmlPluginData); + } else { + callback(new Error('LocaleHtmlPlugin: Unable find root div element. Please ' + + 'verify it exists within your HTML template.'), htmlPluginData); + } + }); + } + }); + + // Report any failed locale prerenders at the compiler level to fail the build. + compiler.plugin('after-compile', function(compilation, callback) { + if(status.failed.length>0) { + callback(new Error('LocaleHtmlPlugin: Failed to prerender localized HTML for ' + + status.failed.join(', '))); + } else { + callback(); + } + }); +}; + +module.exports = LocaleHtmlPlugin; diff --git a/global-cli/modifiers/util/PrerenderPlugin.js b/global-cli/modifiers/util/PrerenderPlugin.js index ee1ae670..1eec1d31 100644 --- a/global-cli/modifiers/util/PrerenderPlugin.js +++ b/global-cli/modifiers/util/PrerenderPlugin.js @@ -1,62 +1,122 @@ var - path = require('path'), fs = require('fs'), + path = require('path'), chalk = require('chalk'), - requireFromString = require('require-from-string'); + vdomRender = require('./vdom-server-render'); + +// Determine if it's a NodeJS output filesystem or if it's a foreign/virtual one. +function isNodeOutputFS(compiler) { + return (compiler.outputFileSystem + && compiler.outputFileSystem.constructor + && compiler.outputFileSystem.constructor.name + && compiler.outputFileSystem.constructor.name === 'NodeOutputFileSystem'); +} + +// Replace the contents of the root div in an HTML string. +function replaceRootDiv(html, start, end, replacement) { + if(/^]+id="root"/i.test(html.substring(start, end+7))) { + return html.substring(0, start) + replacement + html.substring(end+6); + } + var a = html.indexOf('', end); + if(a>=0 && b>=0 && a', '
' + code + '
'); - } catch(e) { - console.log(); - console.log(chalk.yellow('Unable to generate prerender of app state HTML')); - console.log('Reason: ' + e.message || e); - if(e.stack) { - console.log(e.stack); + return meta; + }); + + // Force HtmlWebpackPlugin to use body inject format and set aside the js assets. + compilation.plugin('html-webpack-plugin-before-html-processing', function(htmlPluginData, callback) { + if(!status.err) { + htmlPluginData.plugin.options.inject = 'body'; + jsAssets = htmlPluginData.assets.js; + htmlPluginData.assets.js = []; + } + callback(null, htmlPluginData); + }); + + // Use the prerendered-startup.js to asynchronously add the js assets at load time and embed that + // script inline in the HTML head. + compilation.plugin('html-webpack-plugin-alter-asset-tags', function(htmlPluginData, callback) { + if(!status.err) { + var startup = fs.readFileSync(path.join(__dirname, 'prerendered-startup.txt'), {encoding:'utf8'}); + startup = '\n\t\t' + startup.replace('%SCREENTYPES%', JSON.stringify(opts.screenTypes)) + .replace('%JSASSETS%', JSON.stringify(jsAssets)).replace(/[\n\r]+(.)/g, '\n\t\t$1') + .replace(/[\n\r]+$/, '\n\t'); + htmlPluginData.head.unshift({ + tagName: 'script', + closeTag: true, + attributes: { + type: 'text/javascript' + }, + innerHTML: startup + }); + } + callback(null, htmlPluginData); + }); + + // Replace the contents of the root div with our prerendered result as necessary. + compilation.plugin('html-webpack-plugin-after-html-processing', function(htmlPluginData, callback) { + if(!status.err) { + var html = replaceRootDiv(htmlPluginData.html, 0, htmlPluginData.html.length-6, '
' + + status.prerender + '
'); + if(html) { + htmlPluginData.html = html; + } else { + compilation.errors.push(new Error('PrerenderPlugin: Unable find root div element. Please ' + + 'verify it exists within your HTML template.')); } - console.log(); - console.log('Continuing build without prerendering...'); } - callback && callback(); + callback(null, htmlPluginData); }); } }); }; + +module.exports = PrerenderPlugin; diff --git a/global-cli/modifiers/util/SnapshotPlugin.js b/global-cli/modifiers/util/SnapshotPlugin.js index 148f6c0f..617ba947 100644 --- a/global-cli/modifiers/util/SnapshotPlugin.js +++ b/global-cli/modifiers/util/SnapshotPlugin.js @@ -6,14 +6,12 @@ var findCacheDir = require('find-cache-dir'), chalk = require('chalk'); +// Determine if it's a NodeJS output filesystem or if it's a foreign/virtual one. function isNodeOutputFS(compiler) { - try { - var NodeOutputFileSystem = require('webpack/lib/node/NodeOutputFileSystem'); - return (compiler.outputFileSystem.writeFile===NodeOutputFileSystem.prototype.writeFile); - } catch(e) { - console.error('SnapshotPlugin loader is not compatible with standalone global installs of Webpack.'); - return false; - } + return (compiler.outputFileSystem + && compiler.outputFileSystem.constructor + && compiler.outputFileSystem.constructor.name + && compiler.outputFileSystem.constructor.name === 'NodeOutputFileSystem'); } function getBlobName(args) { @@ -25,19 +23,6 @@ function getBlobName(args) { return 'snapshot_blob.bin'; } -function updateAppInfo(output, blob) { - var appInfo = path.join(output, 'appinfo.json'); - if(exists(appInfo)) { - try { - var meta = JSON.parse(fs.readFileSync(appInfo, {encoding:'utf8'})); - meta.v8SnapshotFile = blob; - fs.writeFileSync(appInfo, JSON.stringify(meta, null, '\t'), {encoding:'utf8'}); - } catch(e) { - return new Error('Failed to set "v8SnapshotFile" property in appinfo.json'); - } - } -} - function SnapshotPlugin(options) { this.options = options || {}; this.options.exec = this.options.exec || process.env.V8_MKSNAPSHOT; @@ -46,18 +31,30 @@ function SnapshotPlugin(options) { '--random-seed=314159265', '--startup-blob=snapshot_blob.bin' ]; + if(process.env.V8_SNAPSHOT_ARGS) { + this.options.args = process.env.V8_SNAPSHOT_ARGS.split(/\s+/); + } this.options.args.push(this.options.target || 'main.js'); } module.exports = SnapshotPlugin; SnapshotPlugin.prototype.apply = function(compiler) { var opts = this.options; + opts.blob = getBlobName(opts.args); + + // Record the v8 blob file in the root appinfo if applicable + compiler.plugin('compilation', function(compilation) { + compilation.plugin('webos-meta-root-appinfo', function(meta) { + meta.v8SnapshotFile = opts.blob; + return meta; + }); + }); compiler.plugin('after-emit', function(compilation, callback) { if(isNodeOutputFS(compiler) && opts.exec) { var ssCache = path.join(findCacheDir({ - name: 'enact-dev', - create: true - }), 'snapshot-target.js'); + name: 'enact-dev', + create: true + }), 'snapshot-target.js'); // Append anything optional to the js to be included in the snapshot if(opts.prepend || opts.append) { @@ -77,15 +74,13 @@ SnapshotPlugin.prototype.apply = function(compiler) { if(child.status === 0) { // Add snapshot to the compilation assets array for stats purposes - var blob = getBlobName(opts.args); try { - var stat = fs.statSync(path.join(compiler.options.output.path, blob)); + var stat = fs.statSync(path.join(compiler.options.output.path, opts.blob)); if(stat.size>0) { - compilation.assets[blob] = { + compilation.assets[opts.blob] = { size: function() { return stat.size; }, emitted: true }; - err = updateAppInfo(compiler.options.output.path, blob); } else { // Temporary fix: mksnapshot may create a 0-byte blob on error err = new Error(child.stdout + '\n' + child.stderr); diff --git a/global-cli/modifiers/util/config-helper.js b/global-cli/modifiers/util/config-helper.js index e01b8e3d..30e054cd 100644 --- a/global-cli/modifiers/util/config-helper.js +++ b/global-cli/modifiers/util/config-helper.js @@ -4,7 +4,7 @@ module.exports = { if(config && config.module && config.module.loaders && name) { for(var i=0; i - - - - - - <%= htmlWebpackPlugin.options.title %> - - <% for(var i=0; i<% } %> - - -
- - - - \ No newline at end of file diff --git a/global-cli/modifiers/util/locales-signage.json b/global-cli/modifiers/util/locales-signage.json new file mode 100644 index 00000000..04f8b880 --- /dev/null +++ b/global-cli/modifiers/util/locales-signage.json @@ -0,0 +1,24 @@ +{ + "paths": [ + "cs/CZ", + "da/DK", + "de/DE", + "en/US", + "es/ES", + "el/GR", + "fr/FR", + "it/IT", + "nl/NL", + "nb/NO", + "pt/PT", + "pt/BR", + "ru/RU", + "fi/FI", + "sv/SE", + "ko/KR", + "zh/Hans/CN", + "ja/JP", + "zh/Hant/HK", + "ar/SA" + ] +} diff --git a/global-cli/modifiers/util/locales-tv.json b/global-cli/modifiers/util/locales-tv.json new file mode 100644 index 00000000..273bef37 --- /dev/null +++ b/global-cli/modifiers/util/locales-tv.json @@ -0,0 +1,75 @@ +{ + "paths": [ + "ar/SA", + "bg/BG", + "bs/Latn/BA", + "cs/CZ", + "da/DK", + "de/DE", + "el/GR", + "en/CN", + "en/GB", + "en/TW", + "en/US", + "en/IN", + "es/CO", + "es/ES", + "et/EE", + "fa/IR", + "fi/FI", + "fr/CA", + "fr/FR", + "ga/IE", + "he/IL", + "hi/IN", + "hr/HR", + "hu/HU", + "id/ID", + "it/IT", + "ja/JP", + "kk/Cyrl/KZ", + "ko/KR", + "ku/Arab/IQ", + "lt/LT", + "lv/LV", + "mk/MK", + "ms/MY", + "nb/NO", + "nl/NL", + "pl/PL", + "pt/BR", + "pt/PT", + "ro/RO", + "ru/RU", + "sk/SK", + "sl/SI", + "sq/AL", + "sr/Latn/RS", + "sv/SE", + "th/TH", + "tr/TR", + "uk/UA", + "uz/Latn/UZ", + "vi/VN", + "zh/Hans/CN", + "zh/Hant/HK", + "zh/Hant/TW", + "as/IN", + "bn/IN", + "gu/IN", + "kn/IN", + "ml/IN", + "mr/IN", + "pa/IN", + "ta/IN", + "te/IN", + "ur/IN", + "mn/Cyrl/MN", + "af/ZA", + "am/ET", + "ha/Latn/NG", + "or/IN", + "az/Latn/AZ", + "km/KH" + ] +} diff --git a/global-cli/modifiers/util/prerendered-startup.txt b/global-cli/modifiers/util/prerendered-startup.txt new file mode 100644 index 00000000..63b511e2 --- /dev/null +++ b/global-cli/modifiers/util/prerendered-startup.txt @@ -0,0 +1,51 @@ + + (function() { + // Initialize font scaling for resolution independence. + var screenTypes = %SCREENTYPES%; + var defaultType = {name: 'standard', pxPerRem: 16, width: window.innerWidth, height: window.innerHeight, aspectRatioName: 'standard', base: true}; + if(screenTypes.length===0) { + screenTypes.push(defaultType); + } + var height = window.innerHeight, + width = window.innerWidth; + var scrObj = screenTypes[screenTypes.length - 1]; + if(height > width) { + width = height; + } + for(var i=screenTypes.length-1; i>=0; i--) { + if(width <= screenTypes[i].width) { + scrObj = screenTypes[i]; + } + } + document.documentElement.style.fontSize = scrObj.pxPerRem + 'px'; + + window.onload = function() { setTimeout(function() { + if(typeof App === 'undefined') { + // Add script nodes, loading the chunks sequentially. + var appendScripts = function(js) { + if(js.length>0) { + var src = js.shift(); + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = src; + script.onload = function() { + appendScripts(js); + }; + document.body.appendChild(script); + } + }; + appendScripts(%JSASSETS%); + } else { + // V8 snapshot, so update the javascript environment then render. + if(typeof updateEnvironment === 'function') { + updateEnvironment(); + } + if(typeof App === 'object' && (typeof ReactDOM === 'object')) { + ReactDOM.render(App['default'] || App, document.getElementById('root')); + } else { + console.log('ERROR: Snapshot app not found'); + } + } + }, 0); }; + })(); + \ No newline at end of file diff --git a/global-cli/modifiers/util/snapshot-helper.js b/global-cli/modifiers/util/snapshot-helper.js index 411fbfc5..c712c83f 100644 --- a/global-cli/modifiers/util/snapshot-helper.js +++ b/global-cli/modifiers/util/snapshot-helper.js @@ -1,12 +1,12 @@ /* * snapshot-helper.js * - * Since the initial ExecutionEnvironment is set during snapshot creation, we need to provide an API - * to update environment on window load. - * + * An exposed utility function to update the javascript environment to the active window to account for any + * launch-time issues when using code created in a snapshot blob. */ global.updateEnvironment = function() { + // Update fbjs to have the correct execution environment for the active window. var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment'); var canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement); @@ -15,4 +15,18 @@ global.updateEnvironment = function() { ExecutionEnvironment.canUseEventListeners = canUseDOM && !!(window.addEventListener || window.attachEvent); ExecutionEnvironment.canUseViewport = canUseDOM && !!window.screen; ExecutionEnvironment.isInWorker = !canUseDOM; // For now, this is true - might change in the future. + + // Mark the iLib localestorage cache as needing re-validation. + var ilib = require('@enact/i18n/ilib/lib/ilib'); + if (ilib._load) { + ilib._load._cacheValidated = false; + } + + // Clear the active resBundle and string cache. + var resBundle = require('@enact/i18n/src/resBundle'); + resBundle.clearResBundle(); + + // Update the iLib/Enact locale to the active window's locale. + var locale = require('@enact/i18n/locale'); + locale.updateLocale(); }; diff --git a/global-cli/modifiers/util/vdom-server-render.js b/global-cli/modifiers/util/vdom-server-render.js new file mode 100644 index 00000000..a186a7b0 --- /dev/null +++ b/global-cli/modifiers/util/vdom-server-render.js @@ -0,0 +1,82 @@ +/* + * vdom-server-render.js + * + * Uses a domserver component like react-dom/server to render the HTML string + * for a given javascript virtualdom Enact codebase. + */ + +var path = require('path'), + nodeFetch = require('node-fetch'), + vm = require('vm'), + FileXHR = require('./FileXHR'); + +require('console.mute'); + +// Setup a generic shared context to run App code within +var m = { + exports:{} +}; +var sandbox = Object.assign({ + require: require, + module: m, + exports: m.exports, + __dirname: process.cwd(), + __filename: 'main.js', + fetch: nodeFetch, + Response: nodeFetch.Response, + Headers: nodeFetch.Headers, + Request: nodeFetch.Request +}, global); +var context = vm.createContext(sandbox); + +/* + Options: + server ReactDomServer or server with compatible APIs + code Javascript sourcecode string + file Filename to designate the code from in NodeJS (visually noted within thrown errors) + locale Specific locale to use in rendering + externals filepath to external Enact framework to use with rendering +*/ +module.exports = function(opts) { + var rendered; + + if(opts.locale) { + sandbox.XMLHttpRequest = FileXHR; + } else { + delete sandbox.XMLHttpRequest; + } + + try { + console.mute(); + + if(opts.externals) { + opts.externals = path.resolve(path.join(opts.externals, 'enact.js')); + // Add external Enact framework filepath if it's used. + opts.code = opts.code.replace(/require\(["']enact_framework["']\)/g, 'require("' + opts.externals + '")'); + // Ensure locale switching support is loaded globally with external framework usage. + var framework = require(opts.externals); + sandbox.iLibLocale = framework('@enact/i18n/locale'); + } else { + delete sandbox.iLibLocale + } + + m.exports = {}; + vm.runInContext(opts.code, context, { + filename: opts.file, + displayErrors: true + }); + + // Update locale if needed. + if(opts.locale && sandbox.iLibLocale && sandbox.iLibLocale.updateLocale) { + sandbox.iLibLocale.updateLocale(opts.locale); + } + + rendered = opts.server.renderToString(m.exports['default'] || m.exports); + + console.resume(); + } catch(e) { + console.resume(); + throw e; + } + return rendered; +}; diff --git a/global-cli/pack.js b/global-cli/pack.js index 3c55cc2a..7aec722e 100755 --- a/global-cli/pack.js +++ b/global-cli/pack.js @@ -53,7 +53,6 @@ function printFileSizes(stats, previousSizeMap) { var assets = stats.toJson().assets .filter(asset => /\.(js|css|bin)$/.test(asset.name)) .map(asset => { - var fileContents = fs.readFileSync('./dist/' + asset.name); var size = fs.statSync('./dist/' + asset.name).size; var previousSize = previousSizeMap[shortFilename(asset.name)]; var difference = getDifferenceLabel(size, previousSize); @@ -75,16 +74,14 @@ function printFileSizes(stats, previousSizeMap) { var rightPadding = ' '.repeat(longestSizeLabelLength - sizeLength); sizeLabel += rightPadding; } - console.log( - ' ' + sizeLabel + - ' ' + chalk.dim(asset.folder + path.sep) + chalk.cyan(asset.name) - ); + console.log(' ' + sizeLabel + ' ' + chalk.dim(asset.folder + path.sep) + + chalk.cyan(asset.name)); }); } // Create the build and optionally, print the deployment instructions. -function build(config, previousSizeMap, guided) { - if(process.env.NODE_ENV === 'development') { +function build(config, previousSizeMap) { + if (process.env.NODE_ENV === 'development') { console.log('Creating a development build...'); } else { console.log('Creating an optimized production build...'); @@ -101,29 +98,22 @@ function build(config, previousSizeMap, guided) { printFileSizes(stats, previousSizeMap); console.log(); console.log(chalk.green('Compiled successfully.')); - console.log(); - - if(guided) { - var openCommand = process.platform === 'win32' ? 'start' : 'open'; - console.log('The ' + chalk.cyan('dist') + ' directory is ready to be deployed.'); - console.log('You may also serve it locally with a static server:'); - console.log(); - console.log(' ' + chalk.cyan('npm') + ' install -g pushstate-server'); - console.log(' ' + chalk.cyan('pushstate-server') + ' dist'); - console.log(' ' + chalk.cyan(openCommand) + ' http://localhost:9000'); - console.log(); + if (process.env.NODE_ENV === 'development') { + console.log(chalk.yellow('NOTICE: This build contains debugging functionality and may run' + + ' slower than in production mode.')); } + console.log(); }); } // Create the build and watch for changes. function watch(config) { - if(process.env.NODE_ENV === 'development') { + if (process.env.NODE_ENV === 'development') { console.log('Creating a development build and watching for changes...'); } else { console.log('Creating an optimized production build and watching for changes...'); } - webpack(config).watch({}, (err, stats) => { + webpack(config).watch({}, (err) => { if (err) { console.error('Failed to create ' + process.env.NODE_ENV + ' build. Reason:'); console.error(err.message || err); @@ -142,7 +132,7 @@ function displayHelp() { console.log(' -w, --watch Rebuild on file changes'); console.log(' -p, --production Build in production mode'); console.log(' -i, --isomorphic Use isomorphic code layout'); - console.log(' (Includes prerendering)'); + console.log(' (includes prerendering)'); console.log(' -v, --version Display version information'); console.log(' -h, --help Display help information'); console.log(); @@ -153,6 +143,12 @@ function displayHelp() { --framework Builds the @enact/*, react, and react-dom into an external framework --externals Specify a local directory path to the standalone external framework --externals-inject Remote public path to the external framework for use injecting into HTML + --locales Specifies to prerender locales. Can be: + "used" - Prerender locales used within ./resources/ + "tv" - Prerender locales supported on the TV platform + "signage" - Prerender locales supported on the signage platform + "ilib" - Prerender all locales that iLib supports + - Prerender the specifically listed locales */ process.exit(0); } @@ -160,17 +156,17 @@ function displayHelp() { module.exports = function(args) { var opts = minimist(args, { boolean: ['minify', 'framework', 's', 'stats', 'p', 'production', 'i', 'isomorphic', 'snapshot', 'w', 'watch', 'h', 'help'], - string: ['externals', 'externals-inject'], + string: ['externals', 'externals-inject', 'locales'], default: {minify:true}, alias: {s:'stats', p:'production', i:'isomorphic', w:'watch', h:'help'} }); - opts.help && displayHelp(); + if (opts.help) displayHelp(); process.env.NODE_ENV = 'development'; var config = devConfig; // Do this as the first thing so that any code reading it knows the right env. - if(opts.production) { + if (opts.production) { process.env.NODE_ENV = 'production'; config = prodConfig; } @@ -178,11 +174,11 @@ module.exports = function(args) { modifiers.apply(config, opts); // Warn and crash if required files are missing - if (!opts.framework && !checkRequiredFiles([config.entry.main[config.entry.main.length-1]])) { + if (!opts.framework && !checkRequiredFiles([config.entry.main[config.entry.main.length - 1]])) { process.exit(1); } - if(opts.watch) { + if (opts.watch) { watch(config); } else { // Read the current file sizes in dist directory. @@ -200,7 +196,7 @@ module.exports = function(args) { // if you're in it, you don't end up in Trash try { rimrafSync('dist/*'); - } catch(e) { + } catch (e) { console.log(chalk.red('Error: ') + ' Unable to delete existing build files. ' + 'Please close any programs currently accessing files within ./dist/.'); console.log(); diff --git a/global-cli/serve.js b/global-cli/serve.js index ac4dfe4e..26d7ddca 100755 --- a/global-cli/serve.js +++ b/global-cli/serve.js @@ -63,7 +63,7 @@ function setupCompiler(host, port, protocol) { // "done" event fires when Webpack has finished recompiling the bundle. // Whether or not you have warnings or errors, you will get this event. compiler.plugin('done', function(stats) { - //clearConsole(); + // clearConsole(); // We have switched off the default Webpack output in WebpackDevServer // options so we are going to "massage" the warnings and errors and present @@ -115,24 +115,19 @@ function setupCompiler(host, port, protocol) { function onProxyError(proxy) { return function(err, req, res){ var host = req.headers && req.headers.host; - console.log( - chalk.red('Proxy error:') + ' Could not proxy request ' + chalk.cyan(req.url) + - ' from ' + chalk.cyan(host) + ' to ' + chalk.cyan(proxy) + '.' - ); - console.log( - 'See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (' + - chalk.cyan(err.code) + ').' - ); + console.log(chalk.red('Proxy error:') + ' Could not proxy request ' + chalk.cyan(req.url) + + ' from ' + chalk.cyan(host) + ' to ' + chalk.cyan(proxy) + '.'); + console.log('See https://nodejs.org/api/errors.html#errors_common_system_errors for more ' + +'information (' + chalk.cyan(err.code) + ').'); console.log(); // And immediately send the proper error response to the client. // Otherwise, the request will eventually timeout with ERR_EMPTY_RESPONSE on the client side. - if (res.writeHead && !res.headersSent) { - res.writeHead(500); + if(res.writeHead && !res.headersSent) { + res.writeHead(500); } - res.end('Proxy error: Could not proxy request ' + req.url + ' from ' + - host + ' to ' + proxy + ' (' + err.code + ').' - ); + res.end('Proxy error: Could not proxy request ' + req.url + ' from ' + host + ' to ' + + proxy + ' (' + err.code + ').'); }; } @@ -166,9 +161,7 @@ function addMiddleware(devServer) { // Modern browsers include text/html into `accept` header when navigating. // However API calls like `fetch()` won’t generally accept text/html. // If this heuristic doesn’t work well for you, don’t use `proxy`. - htmlAcceptHeaders: proxy ? - ['text/html'] : - ['text/html', '*/*'] + htmlAcceptHeaders: proxy ? ['text/html'] : ['text/html', '*/*'] })); if (proxy) { if (typeof proxy !== 'string') { @@ -226,13 +219,13 @@ function runDevServer(host, port, protocol, shouldOpen) { ignored: /node_modules/ }, // Enable HTTPS if the HTTPS environment variable is set to 'true' - https: protocol === "https", + https: protocol === 'https', host: host }); // Our custom middleware proxies requests to /index.html or a remote API. addMiddleware(devServer); // Launch WebpackDevServer. - devServer.listen(port, (err, result) => { + devServer.listen(port, function(err) { if (err) { return console.log(err); } @@ -251,7 +244,7 @@ function runDevServer(host, port, protocol, shouldOpen) { } function run(port, opts) { - var protocol = process.env.HTTPS === 'true' ? "https" : "http"; + var protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; var host = process.env.HOST || opts.host || config.devServer.host || 'localhost'; setupCompiler(host, port, protocol); runDevServer(host, port, protocol, opts.browser); @@ -292,15 +285,14 @@ module.exports = function(args) { // We attempt to use the default port but if it is busy, we offer the user to // run on a different port. `detect()` Promise resolves to the next free port. detect(DEFAULT_PORT).then(port => { - if (port == DEFAULT_PORT) { + if (port === DEFAULT_PORT) { run(port, opts); return; } clearConsole(); - var question = - chalk.yellow('Something is already running on port ' + DEFAULT_PORT + '.') + - '\n\nWould you like to run the app on another port instead?'; + var question = chalk.yellow('Something is already running on port ' + DEFAULT_PORT + '.') + + '\n\nWould you like to run the app on another port instead?'; prompt(question, true).then(shouldChangePort => { if (shouldChangePort) { diff --git a/global-cli/test.js b/global-cli/test.js index 417744dc..a1fc0c5f 100755 --- a/global-cli/test.js +++ b/global-cli/test.js @@ -5,7 +5,7 @@ module.exports = function(args) { args.splice(1, 0, require.resolve('../config/karma.conf.js')); } var child = cp.fork(require.resolve('karma/bin/karma'), args, {env:process.env, cwd:process.cwd()}); - child.on('close', function(code, signal) { + child.on('close', function(code) { process.exit(code); }); }; diff --git a/index.js b/index.js index e9b3956c..09590a64 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,11 @@ module.exports = { create: require('./global-cli/create'), + link: require('./global-cli/link'), pack: require('./global-cli/pack'), serve: require('./global-cli/serve'), clean: require('./global-cli/clean'), lint: require('./global-cli/lint'), test: require('./global-cli/test'), transpile: require('./global-cli/transpile') -} \ No newline at end of file +}; diff --git a/package.json b/package.json index 86901ade..0b70b525 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,17 @@ { "name": "enact-dev", - "version": "0.6.0", + "version": "0.7.0", "description": "Full-featured build environment for Enact applications.", "main": "index.js", "author": "Jason Robitaille ", "repository": "https://github.com/enyojs/enact-dev", - "license": "Apache-2.0", + "license": "BSD-3-Clause", "bin": { "enact": "./bin/enact.js" }, + "scripts": { + "lint": "eslint ." + }, "dependencies": { "autoprefixer": "~6.7.3", "babel-core": "~6.23.1", @@ -25,19 +28,20 @@ "chai": "~3.5.0", "chalk": "~1.1.3", "connect-history-api-fallback": "~1.3.0", + "console.mute": "~0.3.0", "cross-spawn": "~5.0.1", "css-loader": "~0.26.1", "detect-port": "~1.1.0", "dirty-chai": "~1.2.2", - "enyo-console-spy": "enyojs/enyo-console-spy", + "console-snoop": "~0.0.2", "enzyme": "~2.7.1", "es6-map": "~0.1.4", "eslint": "~3.15.0", - "eslint-config-enact": "enyojs/eslint-config-enact#1.1.2", + "eslint-config-enact": "~1.1.5", "eslint-loader": "~1.6.1", "eslint-plugin-babel": "~4.0.1", - "eslint-plugin-enact": "enyojs/eslint-plugin-enact#0.1.1", - "eslint-plugin-react": "~6.10.0", + "eslint-plugin-enact": "~0.1.1", + "eslint-plugin-react": "6.10.0", "expose-loader": "~0.7.3", "extract-text-webpack-plugin": "~1.0.1", "file-loader": "~0.10.0", @@ -45,10 +49,11 @@ "find-cache-dir": "~0.1.1", "fs-extra": "~2.0.0", "glob": "~7.1.1", - "graceful-fs-webpack-plugin": "enyojs/graceful-fs-webpack-plugin#0.1.0", + "global-modules": "~0.2.3", + "graceful-fs-webpack-plugin": "~0.1.0", "html-webpack-plugin": "~2.28.0", "http-proxy-middleware": "~0.17.3", - "ilib-webpack-plugin": "enyojs/ilib-webpack-plugin#0.1.1", + "ilib-webpack-plugin": "~0.1.2", "json-loader": "~0.5.4", "karma": "~1.4.1", "karma-babel-preprocessor": "~6.0.1", @@ -78,7 +83,6 @@ "react-dev-utils": "~0.5.0", "react-dom": "~15.4.2", "recursive-readdir": "~2.1.1", - "require-from-string": "~1.2.1", "resolution-independence": "~0.0.3", "rimraf": "~2.5.4", "semver": "~5.3.0", @@ -88,7 +92,7 @@ "string.prototype.repeat": "~0.2.0", "strip-ansi": "~3.0.1", "style-loader": "~0.13.1", - "webos-meta-webpack-plugin": "enyojs/webos-meta-webpack-plugin#0.2.1", + "webos-meta-webpack-plugin": "~0.3.0", "webpack": "~1.14.0", "webpack-bundle-analyzer": "~2.3.0", "webpack-dev-server": "~1.16.3", diff --git a/template/README.md b/template/README.md index 26642356..8592777a 100644 --- a/template/README.md +++ b/template/README.md @@ -32,6 +32,9 @@ For the project to build, **these files must exist with exact filenames**: You can delete or rename the other files. +You can update the `license` entry in `package.json` to match the license of your choice. For more +information on licenses, please see the [npm documentation](https://docs.npmjs.com/files/package.json#license). + ## Available Scripts In the project directory, you can run: @@ -45,7 +48,7 @@ The page will reload if you make edits.
### `npm run pack` and `npm run pack-p` -Builds the project in the working directory. Specifically, `pack` builds in development mode with code un-minified and with debug code include, whereas `pack-p` builds in production mode, with everything minified and optimized for performance. +Builds the project in the working directory. Specifically, `pack` builds in development mode with code un-minified and with debug code included, whereas `pack-p` builds in production mode, with everything minified and optimized for performance. Be sure to avoid shipping or performance testing on development mode builds. ### `npm run watch` diff --git a/template/package.json b/template/package.json index 302c7782..e7cfdebc 100644 --- a/template/package.json +++ b/template/package.json @@ -28,12 +28,12 @@ "extends": "enact" }, "dependencies": { - "@enact/core": "^1.0.0-beta.3", - "@enact/ui": "^1.0.0-beta.3", - "@enact/moonstone": "^1.0.0-beta.3", - "@enact/spotlight": "^1.0.0-beta.3", - "@enact/i18n": "^1.0.0-beta.3", - "@enact/webos": "^1.0.0-beta.3", + "@enact/core": "^1.0.0", + "@enact/ui": "^1.0.0", + "@enact/moonstone": "^1.0.0", + "@enact/spotlight": "^1.0.0", + "@enact/i18n": "^1.0.0", + "@enact/webos": "^1.0.0", "react": "^15.4.2", "react-dom": "^15.4.2" } diff --git a/template/src/index.js b/template/src/index.js index e04923de..2b5f2ce8 100644 --- a/template/src/index.js +++ b/template/src/index.js @@ -10,7 +10,6 @@ if (typeof window !== 'undefined') { appElement, document.getElementById('root') ); - appElement = null; } export default appElement; \ No newline at end of file