diff --git a/.editorconfig b/.editorconfig index b4ac983e..b21a5918 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,7 +12,7 @@ trim_trailing_whitespace = true [*.html] indent_size = 4 -[elm-package.json] +[elm.json] indent_size = 4 [*.md] diff --git a/.eslintrc.js b/.eslintrc.js index d0c335c9..f894ee6f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,6 +5,9 @@ module.exports = { es6: true, node: true }, + parserOptions: { + ecmaVersion: 6 + }, extends: ['eslint:recommended'], plugins: ['prettier'], rules: { diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..db1bca68 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + singleQuote: true +} diff --git a/.travis.yml b/.travis.yml index b5ec28a4..551b3605 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,6 @@ sudo: required node_js: - '9' - '8' - - '7' - - '6' addons: apt: packages: diff --git a/README.md b/README.md index 5e51ebc1..37c5187d 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Create a production build with `elm-app build` ### Installation -**Node >=6** is required for installation. +**Node >=8** is required for installation. #### Yarn @@ -51,7 +51,7 @@ Create a new `my-app` folder with files for your future project. my-app/ ├── .gitignore ├── README.md -├── elm-package.json +├── elm.json ├── elm-stuff ├── public │ ├── favicon.ico @@ -64,8 +64,7 @@ my-app/ │ ├── main.css │ └── registerServiceWorker.js └── tests - ├── Tests.elm - └── elm-package.json + └── Tests.elm ``` You are ready to employ the full power of Create Elm App! diff --git a/appveyor.yml b/appveyor.yml index f044938f..0a7d942f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,6 +3,13 @@ image: Visual Studio 2017 platform: - x64 +# Test against these versions of Node.js. +environment: + matrix: + # node.js + - nodejs_version: "8" + - nodejs_version: "9" + install: - ps: Install-Product node $env:nodejs_version $env:platform diff --git a/bin/create-elm-app-cli.js b/bin/create-elm-app-cli.js index ed4592c3..c698190f 100755 --- a/bin/create-elm-app-cli.js +++ b/bin/create-elm-app-cli.js @@ -6,7 +6,7 @@ const path = require('path'); const spawn = require('cross-spawn'); const argv = require('minimist')(process.argv.slice(2)); const version = require('../package.json').version; -const elmPlatformVersion = require('elm/package.json').version; +const elmVersion = require('elm/package.json').version; const commands = argv._; if (commands.length === 0) { @@ -14,7 +14,7 @@ if (commands.length === 0) { console.log( 'where is the name of the directory with your future project' ); - console.log('\nElm Platform ' + elmPlatformVersion + '\n'); + console.log('\nElm Platform ' + elmVersion + '\n'); console.log( 'create-elm-app@' + version + ' ' + path.resolve(__dirname, '..') ); diff --git a/bin/elm-app-cli.js b/bin/elm-app-cli.js index 0356dabf..79b1d1ac 100755 --- a/bin/elm-app-cli.js +++ b/bin/elm-app-cli.js @@ -5,9 +5,9 @@ const path = require('path'); const spawn = require('cross-spawn'); const argv = require('minimist')(process.argv.slice(2)); -const executablePaths = require('elm/platform').executablePaths; +const elmExecutable = path.resolve(__dirname, '../node_modules/.bin/elm') const version = require('../package.json').version; -const elmPlatformVersion = require('elm/package.json').version; +const elmVersion = require('elm/package.json').version; const commands = argv._; @@ -37,7 +37,7 @@ switch (script) { args = args.concat([ '--compiler', - path.normalize(executablePaths['elm-make']) + elmExecutable ]); const cp = spawn.sync(require.resolve('elm-test/bin/elm-test'), args, { @@ -51,18 +51,15 @@ switch (script) { break; } case 'install': { - const executable = executablePaths['elm-package']; - spawn.sync(path.normalize(executable), process.argv.slice(2), { + spawn.sync(elmExecutable, process.argv.slice(2), { stdio: 'inherit' }); break; } default: // Proxy elm-platform cli commands. - if (['package', 'reactor', 'make', 'repl'].indexOf(script) !== -1) { - const executable = executablePaths['elm-' + script]; - - spawn.sync(path.normalize(executable), process.argv.slice(3), { + if (['reactor', 'make', 'repl'].indexOf(script) !== -1) { + spawn.sync(elmExecutable, process.argv.slice(2), { stdio: 'inherit' }); break; @@ -84,7 +81,7 @@ function help(version) { console.log( ' create, build, start, install, test, eject, package, reactor, make, repl\n' ); - console.log('\nElm ' + elmPlatformVersion + '\n'); + console.log('\nElm ' + elmVersion + '\n'); console.log( 'create-elm-app@' + version + ' ' + path.resolve(__dirname, '..') ); @@ -94,7 +91,7 @@ function help(version) { * Spawn separate node process with specified script * * @param {string} script Path to script - * @param {Arrays} args Script arguments + * @param {Array} args Script arguments * @return {undefined} */ function spawnSyncNode(script, args) { diff --git a/config/paths.js b/config/paths.js index 83c8ba63..4fb4a412 100644 --- a/config/paths.js +++ b/config/paths.js @@ -46,8 +46,8 @@ module.exports = { dotenv: resolveApp('./.env'), entry: resolveApp('./src/index.js'), appBuild: resolveApp('./build'), - elmPackageJson: resolveApp('./elm-package.json'), - elmMake: require('elm/platform').executablePaths['elm-make'], - publicUrl: getPublicUrl(resolveApp('elm-package.json')), - servedPath: getServedPath(resolveApp('elm-package.json')) + elmJson: resolveApp('./elm.json'), + elm: path.resolve(__dirname, '../node_modules/.bin/elm'), + publicUrl: getPublicUrl(resolveApp('elm.json')), + servedPath: getServedPath(resolveApp('elm.json')) }; diff --git a/config/polyfills.js b/config/polyfills.js new file mode 100644 index 00000000..5c1d8666 --- /dev/null +++ b/config/polyfills.js @@ -0,0 +1,16 @@ +'use strict'; + +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.js'); +} + +// fetch() polyfill for making API calls. +require('whatwg-fetch'); + +// Object.assign() is commonly used with React. +// It will use the native implementation if it's present and isn't buggy. +Object.assign = require('object-assign'); diff --git a/config/webpack.config.dev.js b/config/webpack.config.dev.js index 81751808..58d48b8a 100644 --- a/config/webpack.config.dev.js +++ b/config/webpack.config.dev.js @@ -1,12 +1,11 @@ 'use strict'; -const path = require('path'); const autoprefixer = require('autoprefixer'); -const HotModuleReplacementPlugin = require('webpack/lib/HotModuleReplacementPlugin'); -const DefinePlugin = require('webpack/lib/DefinePlugin'); -const NamedModulesPlugin = require('webpack/lib/NamedModulesPlugin'); -const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); +const path = require('path'); +const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); +const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); const getClientEnvironment = require('./env'); const paths = require('../config/paths'); @@ -20,10 +19,20 @@ const publicUrl = ''; // Get environment variables to inject into our app. const env = getClientEnvironment(publicUrl); +// This is the development configuration. +// It is focused on developer experience and fast rebuilds. +// The production configuration is different and lives in a separate file. module.exports = { + mode: 'development', + // You may want 'eval' instead if you prefer to see the compiled output in DevTools. + // See the discussion in https://github.com/facebook/create-react-app/issues/343. devtool: 'cheap-module-source-map', - + // These are the "entry points" to our application. + // This means they will be the "root" imports that are included in JS bundle. + // The first two entry points enable "hot" CSS and auto-refreshes for JS. entry: [ + // We ship a few polyfills by default: + require.resolve('./polyfills'), // Include an alternative client for WebpackDevServer. A client's job is to // connect to WebpackDevServer by a socket and get notified about changes. // When you save a file, the client will either apply hot updates (in case @@ -42,77 +51,110 @@ module.exports = { paths.appIndexJs ], - output: { + // Add /* filename */ comments to generated require()s in the output. pathinfo: true, - - // The build folder. - path: paths.appBuild, - // This does not produce a real file. It's just the virtual path that is // served by WebpackDevServer in development. This is the JS bundle // containing code from all our entry points, and the Webpack runtime. filename: 'static/js/bundle.js', - + // There are also additional JS chunk files if you use code splitting. + chunkFilename: 'static/js/[name].chunk.js', + // This is the URL that app is served from. We use "/" in development. publicPath: publicPath, - // Point sourcemap entries to original disk location (format as URL on Windows) devtoolModuleFilenameTemplate: info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/') }, - + optimization: { + // Automatically split vendor and commons + // https://twitter.com/wSokra/status/969633336732905474 + // https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366 + splitChunks: { + chunks: 'all', + name: 'vendors' + }, + // Keep the runtime chunk seperated to enable long term caching + // https://twitter.com/wSokra/status/969679223278505985 + runtimeChunk: true + }, resolve: { modules: ['node_modules'], extensions: ['.js', '.elm'] }, - module: { noParse: /\.elm$/, - + strictExportPresence: true, rules: [ + // Disable require.ensure as it's not a standard language feature. + { parser: { requireEnsure: false } }, + { test: /\.js$/, - exclude: [/elm-stuff/, /node_modules/], + exclude: [/[/\\\\]elm-stuff[/\\\\]/, /[/\\\\]node_modules[/\\\\]/], + include: paths.appSrc, loader: require.resolve('babel-loader'), query: { - // Latest stable ECMAScript features presets: [ [ - require.resolve('babel-preset-env'), + require.resolve('@babel/preset-env'), { - targets: { - // React parses on ie 9, so we should too - ie: 9, - // We currently minify with uglify - // Remove after https://github.com/mishoo/UglifyJS2/issues/448 - uglify: true - }, - // Disable polyfill transforms - useBuiltIns: false, + // `entry` transforms `@babel/polyfill` into individual requires for + // the targeted browsers. This is safer than `usage` which performs + // static code analysis to determine what's required. + // This is probably a fine default to help trim down bundles when + // end-users inevitably import '@babel/polyfill'. + useBuiltIns: 'entry', // Do not transform modules to CJS modules: false } ] ], plugins: [ + // Polyfills the runtime needed for async/await and generators [ - require.resolve('babel-plugin-transform-runtime'), + require('@babel/plugin-transform-runtime').default, { helpers: false, - polyfill: false, regenerator: true } ] ] } }, - + // Process any JS outside of the app with Babel. + // Unlike the application JS, we only compile the standard ES features. + { + test: /\.js$/, + use: [ + { + loader: require.resolve('babel-loader'), + options: { + babelrc: false, + compact: false, + presets: [ + [ + // Latest stable ECMAScript features + require('@babel/preset-env').default, + { + // Do not transform modules to CJS + modules: false + } + ] + ], + cacheDirectory: true, + highlightCode: true + } + } + ] + }, { test: /\.elm$/, - exclude: [/elm-stuff/, /node_modules/], + include: paths.appSrc, + exclude: [/[/\\\\]elm-stuff[/\\\\]/, /[/\\\\]node_modules[/\\\\]/], use: [ { - loader: require.resolve('elm-hot-loader') + loader: require.resolve('elm-hot-webpack-loader') }, // string-replace-loader works as InterpolateHtmlPlugin for Elm, // it replaces all of the %PUBLIC_URL% with the URL of your @@ -130,11 +172,10 @@ module.exports = { loader: require.resolve('elm-webpack-loader'), options: { verbose: true, - warn: true, // If ELM_DEBUGGER was set to "false", disable it. Otherwise // for invalid values, "true" and as a default, enable it debug: process.env.ELM_DEBUGGER === 'false' ? false : true, - pathToMake: paths.elmMake, + pathToElm: paths.elm, forceWatch: true } } @@ -146,8 +187,9 @@ module.exports = { // "style" loader turns CSS into JS modules that inject