diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100755 index 0000000000..51c01eb619 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,35 @@ +version: 2 +jobs: + build: + working_directory: /usr/src/app + docker: + - image: banian/node-headless-chrome + steps: + # Checkout repository + - checkout + + # Restore cache + - restore_cache: + key: yarn-{{ checksum "yarn.lock" }} + + # Install dependencies + - run: + name: Install Dependencies + command: NODE_ENV=dev yarn + + # Keep cache + - save_cache: + key: yarn-{{ checksum "yarn.lock" }} + paths: + - "node_modules" + + # Build + - run: + name: Build + command: | + yarn build + + # Test + - run: + name: Tests + command: yarn test && yarn coverage diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000..801d56deaa --- /dev/null +++ b/.eslintignore @@ -0,0 +1,4 @@ +app +node_modules +dist +.nuxt diff --git a/.eslintrc.js b/.eslintrc.js index 987af7e911..69b6b59125 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -22,7 +22,14 @@ module.exports = { // allow debugger during development 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, // do not allow console.logs etc... - 'no-console': 2 + 'no-console': 2, + 'space-before-function-paren': [ + 2, + { + anonymous: 'always', + named: 'never' + } + ], }, globals: {} } diff --git a/.gitignore b/.gitignore index 3395ea12b6..6b2d6f7e1d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,21 @@ -# dependencies +# Dependencies node_modules examples/**/*/yarn.lock +jspm_packages +package-lock.json -# logs +# Logs *.log +npm-debug.log* -# other +# Other .nuxt +.cache # Dist folder dist -# dist example generation +# Dist example generation examples/**/dist # Coverage support @@ -23,3 +27,24 @@ coverage # Intellij idea *.iml .idea + +# OSX +*.DS_Store +.AppleDouble +.LSOverride + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000..9841c0bd3f --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +registry=https://registry.yarnpkg.com diff --git a/.travis.yml b/.travis.yml index 8f0c46e7fb..a92abde5dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,11 @@ language: node_js node_js: - - "8.0" - - "7.2" - - "6.9" -before_install: - - if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi + - "8" + - "6" +cache: + yarn: true + directories: + - node_modules install: - yarn install - yarn run build diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..a91492b217 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at team@nuxtjs.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..966a162f9a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,10 @@ +# Contributing to Nuxt.js + +1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device. +2. Install the dependencies: `npm install`. +3. Run `npm link` to link the local repo to NPM. +4. Run `npm run build` to build or `npm run watch` to build and watch for code changes. +5. Then npm link this repo inside any example app with `npm link nuxt`. +6. Then you can run your example app with the local version of Nuxt.js (You may need to re-run the example app as you change server side code in the Nuxt.js repository). + +Make sure to add tests into `test/` directory and try them with `npm test` before making a pull request. diff --git a/README.md b/README.md index 7c655d5ebc..3b27ba6ea3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

Build Status Windows Build Status - ย Coverage Status + ย Coverage Status Downloads Version License @@ -15,7 +15,7 @@

-> Nuxt.js is a framework for server-rendered Vue applications (inspired by [Next.js](https://github.com/zeit/next.js)) +> Nuxt.js is a Versatile Vue.js Framework ## ๐Ÿšง Under active development, [1.0](https://github.com/nuxt/nuxt.js/projects/1) will be released soon :fire: @@ -98,6 +98,8 @@ Support us with a monthly donation and help us continue our activities. [[Become - ๐Ÿ“˜ Documentation: [https://nuxtjs.org](https://nuxtjs.org) - ๐ŸŽฌ Video: [1 minute demo](https://www.youtube.com/watch?v=kmf-p-pTi40) - ๐Ÿฆ Twitter: [@nuxt_js](https://twitter.com/nuxt_js) +- ๐Ÿ‘ฅ [Nuxt.js Community](https://github.com/nuxt-community) +- ๐Ÿ“ฆ [Nuxt.js Modules](https://github.com/nuxt-community/modules) - ๐Ÿ‘‰ [Play with Nuxt.js online](https://glitch.com/edit/#!/nuxt-hello-world) ## Getting started @@ -157,20 +159,29 @@ Learn more at [nuxtjs.org](https://nuxtjs.org). ## Templates You can start by using one of our starter templates: -- [starter](https://github.com/nuxt/starter): Basic Nuxt.js project template -- [express](https://github.com/nuxt/express): Nuxt.js + Express -- [koa](https://github.com/nuxt/koa): Nuxt.js + Koa -- [adonuxt](https://github.com/nuxt/adonuxt): Nuxt.js + AdonisJS +- [starter](https://github.com/nuxt-community/starter-template): Basic Nuxt.js project template +- [express](https://github.com/nuxt-community/express-template): Nuxt.js + Express +- [koa](https://github.com/nuxt-community/koa-template): Nuxt.js + Koa +- [adonuxt](https://github.com/nuxt-community/adonuxt-template): Nuxt.js + AdonisJS +- [micro](https://github.com/nuxt-community/micro-template): Nuxt.js + Micro +- [nuxtent](https://github.com/nuxt-community/nuxtent-template): Nuxt.js + Nuxtent module for content heavy sites ## Using nuxt.js programmatically ```js -const Nuxt = require('nuxt') +const { Nuxt, Builder } = require('nuxt') -// Launch nuxt build with given options +// Import and set nuxt.js options let config = require('./nuxt.config.js') +config.dev = !(process.env.NODE_ENV === 'production') + let nuxt = new Nuxt(config) +// Start build process (only in development) +if (config.dev) { + new Builder(nuxt).build() +} + // You can use nuxt.render(req, res) or nuxt.renderRoute(route, context) ``` @@ -240,4 +251,7 @@ Note: we recommend putting `.nuxt` in `.npmignore` or `.gitignore`. ## Roadmap -https://github.com/nuxt/nuxt.js/projects/1 +https://trello.com/b/lgy93IOl/nuxtjs-10 + +## Contributing +Please see our [CONTRIBUTING.md](./CONTRIBUTING.md) diff --git a/appveyor.yml b/appveyor.yml index 2dfd73a345..f001912793 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,12 @@ # Test against the latest version of this Node.js version environment: - nodejs_version: "6" + matrix: + - nodejs_version: "6" + - nodejs_version: "8" + +cache: + - "%LOCALAPPDATA%\\Yarn" + - node_modules # Install scripts. (runs after repo cloning) install: diff --git a/benchmarks/README.md b/benchmarks/README.md new file mode 100644 index 0000000000..9623cc1191 --- /dev/null +++ b/benchmarks/README.md @@ -0,0 +1,29 @@ +# Nuxt.js server-side benchmarks + +> Taken from [Next.js benchmarks](https://github.com/zeit/next.js/tree/master/bench), if you like React, we recommend you to try [Next.js](https://github.com/zeit/next.js). + +## Installation + +Follow the steps in [CONTRIBUTING.md](../CONTRIBUTING.md). + +Both benchmarks use `ab`. So make sure you have it installed. + +## Usage + +Before running the test: + +``` +npm run start +``` + +Then run one of these tests: + +- Stateless application which renders `

My component!

`. Runs 3000 http requests. +``` +npm run bench:stateless +``` + +- Stateless application which renders `
  • This is row {i}
  • ` 10.000 times. Runs 500 http requests. +``` +npm run bench:stateless-big +``` \ No newline at end of file diff --git a/benchmarks/package.json b/benchmarks/package.json new file mode 100644 index 0000000000..d3f5220127 --- /dev/null +++ b/benchmarks/package.json @@ -0,0 +1,9 @@ +{ + "name": "nuxt-benchmarks", + "scripts": { + "build": "nuxt build", + "start": "npm run build && nuxt start", + "bench:stateless": "ab -c1 -n3000 http://127.0.0.1:3000/stateless", + "bench:stateless-big": "ab -c1 -n500 http://127.0.0.1:3000/stateless-big" + } +} \ No newline at end of file diff --git a/benchmarks/pages/stateless-big.vue b/benchmarks/pages/stateless-big.vue new file mode 100644 index 0000000000..9011162e55 --- /dev/null +++ b/benchmarks/pages/stateless-big.vue @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/benchmarks/pages/stateless.vue b/benchmarks/pages/stateless.vue new file mode 100644 index 0000000000..5f8615de3c --- /dev/null +++ b/benchmarks/pages/stateless.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/bin/nuxt b/bin/nuxt index 4e9019757d..90bc808599 100755 --- a/bin/nuxt +++ b/bin/nuxt @@ -1,9 +1,10 @@ #!/usr/bin/env node -var join = require('path').join +const join = require('path').join +require('../lib/common/cli/errors') -var defaultCommand = 'dev' -var commands = new Set([ +const defaultCommand = 'dev' +const commands = new Set([ defaultCommand, 'init', 'build', @@ -19,6 +20,6 @@ if (commands.has(cmd)) { cmd = defaultCommand } -var bin = join(__dirname, 'nuxt-' + cmd) +const bin = join(__dirname, 'nuxt-' + cmd) require(bin) diff --git a/bin/nuxt-build b/bin/nuxt-build index 1af998d2be..9222db6df2 100755 --- a/bin/nuxt-build +++ b/bin/nuxt-build @@ -1,63 +1,91 @@ #!/usr/bin/env node +/* eslint-disable no-console */ // Show logs -process.env.DEBUG = 'nuxt:*' +process.env.DEBUG = process.env.DEBUG || 'nuxt:*' -var fs = require('fs') -var without = require('lodash').without -var Nuxt = require('../') -var resolve = require('path').resolve +const fs = require('fs') +const parseArgs = require('minimist') +const { Nuxt, Builder, Generator } = require('../') +const resolve = require('path').resolve +const debug = require('debug')('nuxt:build') +debug.color = 2 // Force green color -// --analyze option -var analyzeBuild = false -if (process.argv.indexOf('--analyze') !== -1 || process.argv.indexOf('-a') !== -1) { - analyzeBuild = true - process.argv = without(process.argv, '--analyze', '-a') +const argv = parseArgs(process.argv.slice(2), { + alias: { + h: 'help', + c: 'config-file', + a: 'analyze', + s: 'spa', + u: 'universal' + }, + boolean: ['h', 'a', 's', 'u'], + string: ['c'], + default: { + c: 'nuxt.config.js' + } +}) + +if (argv.help) { + console.log(` + Description + Compiles the application for production deployment + Usage + $ nuxt build + Options + --analyze, -a Launch webpack-bundle-analyzer to optimize your bundles. + --spa Launch in SPA mode + --universal Launch in Universal mode (default) + --config-file, -c Path to Nuxt.js config file (default: nuxt.config.js) + --help, -h Displays this message + `) + process.exit(0) } -var nuxtConfigFileName = 'nuxt.config.js' - -// --config-file option -var indexOfConfig = false -if (process.argv.indexOf('--config-file') !== -1) { - indexOfConfig = process.argv.indexOf('--config-file') -} else if (process.argv.indexOf('-c') !== -1) { - indexOfConfig = process.argv.indexOf('-c') -} - -if (indexOfConfig !== false) { - nuxtConfigFileName = process.argv.slice(indexOfConfig)[1] - process.argv = without(process.argv, '--config-file', '-c', nuxtConfigFileName) -} - -// Root directory parameter -var rootDir = resolve(process.argv.slice(2)[0] || '.') -var nuxtConfigFilePath = resolve(rootDir, nuxtConfigFileName) +const rootDir = resolve(argv._[0] || '.') +const nuxtConfigFile = resolve(rootDir, argv['config-file']) var options = {} -if (fs.existsSync(nuxtConfigFilePath)) { - options = require(nuxtConfigFilePath) -} else { - console.log(`Could not locate ${nuxtConfigFilePath}`) // eslint-disable-line no-console +if (fs.existsSync(nuxtConfigFile)) { + options = require(nuxtConfigFile) +} else if (argv['config-file'] !== 'nuxt.config.js') { + console.error(`> Could not load config file ${argv['config-file']}`) + process.exit(1) } - if (typeof options.rootDir !== 'string') { options.rootDir = rootDir } -options.dev = false // Create production build when calling `nuxt build` +// Create production build when calling `nuxt build` +options.dev = false +// Nuxt Mode +options.mode = (argv['spa'] && 'spa') || (argv['universal'] && 'universal') || options.mode + +// Analyze option options.build = options.build || {} -if (analyzeBuild) { - options.build.analyze = analyzeBuild +if (argv.analyze) { + options.build.analyze = true } -console.log('[nuxt] Building...') // eslint-disable-line no-console -var nuxt = module.exports = new Nuxt(options) -nuxt.build() - .then(() => { - console.log('[nuxt] Building done') // eslint-disable-line no-console - }) - .catch((err) => { - console.error(err) // eslint-disable-line no-console - process.exit(1) +debug('Building...') +const nuxt = new Nuxt(options) +const builder = new Builder(nuxt) + +if (options.mode !== 'spa') { + // Build for SSR app + builder.build() + .then(() => debug('Building done')) + .catch((err) => { + console.error(err) + process.exit(1) + }) +} else { + // Disable minify to get exact results of nuxt start + nuxt.options.generate.minify = false + // Generate on spa mode + new Generator(nuxt, builder).generate({ build: true }).then(() => { + if (!nuxt.options.dev) { + console.log(`โœ“ You can now directly upload ${nuxt.options.generate.dir}/ or start server using "nuxt start"`) + } }) +} diff --git a/bin/nuxt-dev b/bin/nuxt-dev index 4b1f733ce6..5d0dda8be7 100755 --- a/bin/nuxt-dev +++ b/bin/nuxt-dev @@ -1,80 +1,129 @@ #!/usr/bin/env node +/* eslint-disable no-console */ // Show logs -process.env.DEBUG = 'nuxt:*' +process.env.DEBUG = process.env.DEBUG || 'nuxt:*' -var _ = require('lodash') -var debug = require('debug')('nuxt:build') +const _ = require('lodash') +const debug = require('debug')('nuxt:build') debug.color = 2 // force green color -var fs = require('fs') -var Nuxt = require('../') -var chokidar = require('chokidar') -var resolve = require('path').resolve -var without = require('lodash').without +const fs = require('fs') +const parseArgs = require('minimist') +const { Nuxt, Builder } = require('../') +const chokidar = require('chokidar') +const path = require('path') +const resolve = path.resolve +const pkg = require(path.join('..', 'package.json')) -var nuxtConfigFileName = 'nuxt.config.js' +const argv = parseArgs(process.argv.slice(2), { + alias: { + h: 'help', + H: 'hostname', + p: 'port', + c: 'config-file', + s: 'spa', + u: 'universal', + v: 'version' + }, + boolean: ['h', 's', 'u', 'v'], + string: ['H', 'c'], + default: { + c: 'nuxt.config.js' + } +}) -// --config-file option -var indexOfConfig = false -if (process.argv.indexOf('--config-file') !== -1) { - indexOfConfig = process.argv.indexOf('--config-file') -} else if (process.argv.indexOf('-c') !== -1) { - indexOfConfig = process.argv.indexOf('-c') +if (argv.version) { + console.log(pkg.version) + process.exit(0) } -if (indexOfConfig !== false) { - nuxtConfigFileName = process.argv.slice(indexOfConfig)[1] - process.argv = without(process.argv, '--config-file', '-c', nuxtConfigFileName) +if (argv.hostname === '') { + console.error(`> Provided hostname argument has no value`) + process.exit(1) } -var rootDir = resolve(process.argv.slice(2)[0] || '.') -var nuxtConfigFile = resolve(rootDir, nuxtConfigFileName) - -var options = {} -if (fs.existsSync(nuxtConfigFile)) { - options = require(nuxtConfigFile) +if (argv.help) { + console.log(` + Description + Starts the application in development mode (hot-code reloading, error + reporting, etc) + Usage + $ nuxt dev -p -H + Options + --port, -p A port number on which to start the application + --hostname, -H Hostname on which to start the application + --spa Launch in SPA mode + --universal Launch in Universal mode (default) + --config-file, -c Path to Nuxt.js config file (default: nuxt.config.js) + --help, -h Displays this message + `) + process.exit(0) } -if (typeof options.rootDir !== 'string') { - options.rootDir = rootDir -} -options.dev = true // Add hot reloading and watching changes -var nuxt = module.exports = new Nuxt(options) -var port = process.env.PORT || process.env.npm_package_config_nuxt_port -var host = process.env.HOST || process.env.npm_package_config_nuxt_host -var server = nuxt.server = new nuxt.Server(nuxt).listen(port, host) +const rootDir = resolve(argv._[0] || '.') +const nuxtConfigFile = resolve(rootDir, argv['config-file']) -listenOnConfigChanges(nuxt, server) +// Load config once for chokidar +const nuxtConfig = loadNuxtConfig() +_.defaultsDeep(nuxtConfig, { watchers: { chokidar: { ignoreInitial: true } } }) -function listenOnConfigChanges(nuxt, server) { - // Listen on nuxt.config.js changes - var build = _.debounce(() => { +// Start dev +let dev = startDev() + +// Start watching for nuxt.config.js changes +chokidar + .watch(nuxtConfigFile, nuxtConfig.watchers.chokidar) + .on('all', _.debounce(() => { debug('[nuxt.config.js] changed') - delete require.cache[nuxtConfigFile] - var options = {} - if (fs.existsSync(nuxtConfigFile)) { - try { - options = require(nuxtConfigFile) - } catch (e) { - return console.error(e) // eslint-disable-line no-console - } - } - options.rootDir = rootDir - nuxt.close() - .then(() => { - nuxt.renderer = null - debug('Rebuilding the app...') - return new Nuxt(options).build() - }) - .then((nuxt) => { - server.nuxt = nuxt - }) - .catch((error) => { - console.error('Error while rebuild the app:', error) // eslint-disable-line no-console - process.exit(1) - }) - }, 200) - var nuxtConfigFile = resolve(rootDir, nuxtConfigFileName) - chokidar.watch(nuxtConfigFile, Object.assign({}, nuxt.options.watchers.chokidar, {ignoreInitial: true})) - .on('all', build) + debug('Rebuilding the app...') + dev = dev.then(startDev) + }), 2500) + +function startDev(oldNuxt) { + // Get latest environment variables + const port = argv.port || process.env.PORT || process.env.npm_package_config_nuxt_port + const host = argv.hostname || process.env.HOST || process.env.npm_package_config_nuxt_host + + // Load options + let options = {} + try { + options = loadNuxtConfig() + } catch (err) { + console.error(err) + return // Wait for next reload + } + + // Create nuxt and builder instance + const nuxt = new Nuxt(options) + const builder = new Builder(nuxt) + + return Promise.resolve() + .then(() => builder.build()) // 1- Start build + .then(() => oldNuxt ? oldNuxt.close() : Promise.resolve()) // 2- Close old nuxt after successful build + .then(() => nuxt.listen(port, host)) // 3- Start listening + .then(() => nuxt) // 4- Pass new nuxt to watch chain +} + +function loadNuxtConfig() { + let options = {} + + if (fs.existsSync(nuxtConfigFile)) { + delete require.cache[nuxtConfigFile] + options = require(nuxtConfigFile) + } else if (argv['config-file'] !== 'nuxt.config.js') { + console.error(`> Could not load config file ${argv['config-file']}`) + process.exit(1) + } + + if (typeof options.rootDir !== 'string') { + options.rootDir = rootDir + } + + // Force development mode for add hot reloading and watching changes + options.dev = true + + // Nuxt Mode + options.mode = (argv['spa'] && 'spa') || (argv['universal'] && 'universal') || options.mode + + return options } diff --git a/bin/nuxt-generate b/bin/nuxt-generate index c187810fa1..1d9ace2e91 100755 --- a/bin/nuxt-generate +++ b/bin/nuxt-generate @@ -1,31 +1,82 @@ #!/usr/bin/env node +/* eslint-disable no-console */ // Show logs -process.env.DEBUG = 'nuxt:*' +process.env.DEBUG = process.env.DEBUG || 'nuxt:*' -var fs = require('fs') -var Nuxt = require('../') -var resolve = require('path').resolve +const fs = require('fs') +const parseArgs = require('minimist') +const debug = require('debug')('nuxt:generate') -var rootDir = resolve(process.argv.slice(2)[0] || '.') -var nuxtConfigFile = resolve(rootDir, 'nuxt.config.js') +const { Nuxt, Builder, Generator } = require('../') +const resolve = require('path').resolve + +const argv = parseArgs(process.argv.slice(2), { + alias: { + h: 'help', + c: 'config-file', + s: 'spa', + u: 'universal' + }, + boolean: ['h', 's', 'u', 'build'], + string: ['c'], + default: { + c: 'nuxt.config.js', + build: true + } +}) + +if (argv.help) { + console.log(` + Description + Generate a static web application (server-rendered) + Usage + $ nuxt generate + Options + --spa Launch in SPA mode + --spa Launch in SPA mode + --universal Launch in Universal mode (default) + --config-file, -c Path to Nuxt.js config file (default: nuxt.config.js) + --help, -h Displays this message + --no-build Just run generate for faster builds when just dynamic routes changed. Nuxt build is needed before this command. + `) + process.exit(0) +} + +const rootDir = resolve(argv._[0] || '.') +const nuxtConfigFile = resolve(rootDir, argv['config-file']) var options = {} if (fs.existsSync(nuxtConfigFile)) { options = require(nuxtConfigFile) +} else if (argv['config-file'] !== 'nuxt.config.js') { + console.error(`> Could not load config file ${argv['config-file']}`) + process.exit(1) } if (typeof options.rootDir !== 'string') { options.rootDir = rootDir } options.dev = false // Force production mode (no webpack middleware called) -console.log('[nuxt] Generating...') // eslint-disable-line no-console -var nuxt = module.exports = new Nuxt(options) -nuxt.generate() +// Nuxt Mode +options.mode = (argv['spa'] && 'spa') || (argv['universal'] && 'universal') || options.mode + +debug('Generating...') +const nuxt = new Nuxt(options) +const builder = new Builder(nuxt) +const generator = new Generator(nuxt, builder) + +const generateOptions = { + init: true, + build: argv['build'] +} + +generator.generate(generateOptions) .then(() => { - console.log('[nuxt] Generate done') // eslint-disable-line no-console + debug('Generate done') + process.exit(0) }) .catch((err) => { - console.error(err) // eslint-disable-line no-console + console.error(err) process.exit(1) }) diff --git a/bin/nuxt-start b/bin/nuxt-start index 8af26fbaf4..cc18b1dada 100755 --- a/bin/nuxt-start +++ b/bin/nuxt-start @@ -1,22 +1,91 @@ #!/usr/bin/env node +/* eslint-disable no-console */ -var fs = require('fs') -var Nuxt = require('../') -var resolve = require('path').resolve +const fs = require('fs') +const parseArgs = require('minimist') +const { Nuxt } = require('../') +const { resolve } = require('path') -var rootDir = resolve(process.argv.slice(2)[0] || '.') -var nuxtConfigFile = resolve(rootDir, 'nuxt.config.js') +const argv = parseArgs(process.argv.slice(2), { + alias: { + h: 'help', + H: 'hostname', + p: 'port', + c: 'config-file', + s: 'spa', + u: 'universal' + }, + boolean: ['h', 's', 'u'], + string: ['H', 'c'], + default: { + c: 'nuxt.config.js' + } +}) + +if (argv.hostname === '') { + console.error(`> Provided hostname argument has no value`) + process.exit(1) +} + +if (argv.help) { + console.log(` + Description + Starts the application in production mode. + The application should be compiled with \`nuxt build\` first. + Usage + $ nuxt start -p -H + Options + --port, -p A port number on which to start the application + --hostname, -H Hostname on which to start the application + --spa Launch in SPA mode + --universal Launch in Universal mode (default) + --config-file, -c Path to Nuxt.js config file (default: nuxt.config.js) + --help, -h Displays this message + `) + process.exit(0) +} + +const rootDir = resolve(argv._[0] || '.') +const nuxtConfigFile = resolve(rootDir, argv['config-file']) + +let options = {} -var options = {} if (fs.existsSync(nuxtConfigFile)) { options = require(nuxtConfigFile) +} else if (argv['config-file'] !== 'nuxt.config.js') { + console.error(`> Could not load config file ${argv['config-file']}`) + process.exit(1) } + if (typeof options.rootDir !== 'string') { options.rootDir = rootDir } -options.dev = false // Force production mode (no webpack middleware called) -var nuxt = module.exports = new Nuxt(options) -var port = process.env.PORT || process.env.npm_package_config_nuxt_port -var host = process.env.HOST || process.env.npm_package_config_nuxt_host -var server = nuxt.server = new nuxt.Server(nuxt).listen(port, host) +// Force production mode (no webpack middleware called) +options.dev = false + +// Nuxt Mode +options.mode = (argv['spa'] && 'spa') || (argv['universal'] && 'universal') || options.mode + +const nuxt = new Nuxt(options) + +// Check if project is built for production +const distDir = resolve(nuxt.options.rootDir, nuxt.options.buildDir || '.nuxt', 'dist') +if (!fs.existsSync(distDir)) { + console.error('> No build files found, please run `nuxt build` before launching `nuxt start`') + process.exit(1) +} + +// Check if SSR Bundle is required +if (nuxt.options.render.ssr === true) { + const ssrBundlePath = resolve(distDir, 'server-bundle.json') + if (!fs.existsSync(ssrBundlePath)) { + console.error('> No SSR build! Please start with `nuxt start --spa` or build using `nuxt build --universal`') + process.exit(1) + } +} + +const port = argv.port || process.env.PORT || process.env.npm_package_config_nuxt_port +const host = argv.hostname || process.env.HOST || process.env.npm_package_config_nuxt_host + +nuxt.listen(port, host) diff --git a/build/rollup.config.js b/build/rollup.config.js new file mode 100755 index 0000000000..346d3fab8a --- /dev/null +++ b/build/rollup.config.js @@ -0,0 +1,116 @@ +// Some parts brought from https://github.com/vuejs/vue/blob/dev/build/config.js +const { resolve } = require('path') +const rollupBabel = require('rollup-plugin-babel') +const rollupAlias = require('rollup-plugin-alias') +const rollupCommonJS = require('rollup-plugin-commonjs') +const rollupReplace = require('rollup-plugin-replace') +const rollupNodeResolve = require('rollup-plugin-node-resolve') +const packageJson = require('../package.json') + +const dependencies = Object.keys(packageJson.dependencies) +const version = packageJson.version || process.env.VERSION + +// ----------------------------- +// Banner +// ----------------------------- +const banner = + '/*!\n' + + ' * Nuxt.js v' + version + '\n' + + ' * Released under the MIT License.\n' + + ' */' + +// ----------------------------- +// Aliases +// ----------------------------- +const rootDir = resolve(__dirname, '..') +const libDir = resolve(rootDir, 'lib') +const distDir = resolve(rootDir, 'dist') + +const aliases = { + core: resolve(libDir, 'core/index.js'), + builder: resolve(libDir, 'builder/index.js'), + common: resolve(libDir, 'common/index.js'), + utils: resolve(libDir, 'common/utils.js'), + app: resolve(libDir, 'app') +} + +// ----------------------------- +// Builds +// ----------------------------- +const builds = { + nuxt: { + entry: resolve(libDir, 'index.js'), + file: resolve(distDir, 'nuxt.js') + }, + core: { + entry: resolve(libDir, 'core/index.js'), + file: resolve(distDir, 'core.js') + } +} + +// ----------------------------- +// Default config +// ----------------------------- +function genConfig (opts) { + const config = { + input: opts.entry, + output: { + file: opts.file, + format: 'cjs', + sourcemap: true + }, + external: ['fs', 'path', 'http', 'module', 'vue-server-renderer/server-plugin', 'vue-server-renderer/client-plugin'] + .concat(dependencies, opts.external), + banner: opts.banner || banner, + name: opts.modulename || 'Nuxt', + plugins: [ + rollupAlias(Object.assign({ + resolve: ['.js', '.json', '.jsx', '.ts'] + }, aliases, opts.alias)), + + rollupNodeResolve({ preferBuiltins: true }), + + rollupCommonJS(), + + rollupBabel(Object.assign({ + exclude: 'node_modules/**', + plugins: [ + ['transform-runtime', { 'helpers': false, 'polyfill': false }], + 'transform-async-to-generator', + 'array-includes', + 'external-helpers' + ], + presets: [ + ['env', { + targets: { + node: '6.11.0' + }, + modules: false + }] + ], + 'env': { + 'test': { + 'plugins': [ 'istanbul' ] + } + } + }, opts.babel)), + + rollupReplace({ __VERSION__: version }) + ].concat(opts.plugins || []) + } + + if (opts.env) { + config.plugins.push(rollupReplace({ + 'process.env.NODE_ENV': JSON.stringify(opts.env) + })) + } + + return config +} + +if (process.env.TARGET) { + module.exports = genConfig(builds[process.env.TARGET]) +} else { + exports.getBuild = name => genConfig(builds[name]) + exports.getAllBuilds = () => Object.keys(builds).map(name => genConfig(builds[name])) +} diff --git a/build/start.js b/build/start.js new file mode 100755 index 0000000000..9f5eaecc28 --- /dev/null +++ b/build/start.js @@ -0,0 +1,108 @@ +#!/usr/bin/env node + +const now = Date.now() + +const { readFileSync, readJSONSync, writeFileSync, copySync, removeSync } = require('fs-extra') +const { resolve, relative } = require('path') + +// Dirs +const rootDir = resolve(__dirname, '..') +const startDir = resolve(rootDir, 'start') + +// Read main package.json +const packageJSON = readJSONSync(resolve(rootDir, 'package.json')) + +// Required and Excluded packages for start +let requires = [ + 'source-map-support', + 'pretty-error', + 'minimist' +] + +const excludes = [ + 'path', + 'fs', + 'http', + 'module' +].concat(Object.keys(packageJSON.devDependencies)) + +// Parse dist/core.js for all external dependencies +const requireRegex = /require\('([-@/\w]+)'\)/g +const rawCore = readFileSync(resolve(rootDir, 'dist/core.js')) +let match = requireRegex.exec(rawCore) +while (match) { + requires.push(match[1]) + match = requireRegex.exec(rawCore) +} + +// Apply Excludes +requires = requires.filter(r => excludes.indexOf(r) === -1) + +// Resolve version constrains +let dependencies = {} +requires.forEach(r => { + if (!packageJSON.dependencies[r]) { + console.warn('Cannot resolve dependency version for ' + r) + return + } + dependencies[r] = packageJSON.dependencies[r] +}) + +// Drop fields +let drops = ['devDependencies', 'scripts', 'nyc', 'types'] +drops.forEach(k => { + delete packageJSON[k] +}) + +// Update dependencies +packageJSON.dependencies = dependencies + +// Update package meta +packageJSON.name = 'nuxt-start' +packageJSON.description = 'runtime-only build for nuxt' +packageJSON.bin = { + 'nuxt-start': './bin/nuxt-start' +} + +// Update package.json +writeFileSync(resolve(startDir, 'package.json'), JSON.stringify(packageJSON, null, 2)) + +// Copy required files +const excludeFiles = [ + 'README.md', + '.gitignore' +] +packageJSON.files.forEach(file => { + if (excludeFiles.indexOf(file) !== -1) { + return + } + let src = resolve(rootDir, file) + let dst = resolve(startDir, file) + // console.log(relative(rootDir, src), '~>', relative(rootDir, dst)) + removeSync(dst) + copySync(src, dst) +}) + +// Remove extras +const extraFiles = [ + 'bin/nuxt-build', + 'bin/nuxt-generate', + 'bin/nuxt-dev', + 'bin/nuxt', + 'dist/nuxt.js', + 'dist/nuxt.js.map' +] +extraFiles.forEach(file => { + removeSync(resolve(startDir, file)) +}) + +// Patch index.js +const startIndexjs = resolve(startDir, 'index.js') +writeFileSync(startIndexjs, String(readFileSync(startIndexjs)).replace('./dist/nuxt', './dist/core')) + +// Patch bin/nuxt-start +const binStart = resolve(startDir, 'bin/nuxt-start') +writeFileSync(binStart, String(readFileSync(binStart)).replace(/nuxt start/g, 'nuxt-start')) + +const ms = Date.now() - now +console.log(`Generated ${packageJSON.name}@${packageJSON.version} in ${ms}ms`) diff --git a/examples/async-component-injection/assets/css/common.css b/examples/async-component-injection/assets/css/common.css new file mode 100644 index 0000000000..b4f5ee78bf --- /dev/null +++ b/examples/async-component-injection/assets/css/common.css @@ -0,0 +1,69 @@ +body { + font-family: "Roboto", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, serif; + text-rendering: optimizelegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-weight: 400; + font-size: 16px; + word-spacing: 1px; + color: #666; + margin: 0; +} +img { + border: none; +} +a { + color: #666; + text-decoration: none; + transition: color 0.2s ease, border-color 0.2s ease; +} +.header { + letter-spacing: 5px; + margin: 50px auto 15px; + text-align: center; +} +.header a { + font-size: 15px; + color: #444; +} +.links { + text-align: center; + font-family: "Roboto", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, serif; + color: #999; + font-size: 24px; + margin: 0; +} +.links a { + cursor: pointer; + padding: 2px; + margin: 0 3px; +} +.links img { + width: 15px; + height: 15px; +} +.header, +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: "Montserrat", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, sans-serif; + font-weight: 400; + color: #444; +} +.main { + max-width: 600px; + margin: 50px auto; + padding: 0 30px 50px; + position: relative; +} +@media screen and (max-width: 420px) { + .header { + margin: 40px auto 10px; + } + .header a { + font-size: 14px; + } +} diff --git a/examples/async-component-injection/assets/css/index.css b/examples/async-component-injection/assets/css/index.css new file mode 100644 index 0000000000..25870b834d --- /dev/null +++ b/examples/async-component-injection/assets/css/index.css @@ -0,0 +1,57 @@ +.main > ul { + list-style-type: none; + padding: 0; + padding-top: 4px; +} +.main > ul > li { + position: relative; + padding: 30px 0 30px; + border-bottom: 1px solid #e6e6e6; +} +.main > ul > li:first-child { + margin-top: -30px; +} +.main h2, +.main h3 { + letter-spacing: 1px; + margin: 0; + text-transform: uppercase; +} +.main h2 { + font-size: 20px; + letter-spacing: 1px; + margin-left: 120px; +} +.main h2 a { + color: #444; +} +.main h2 a:hover { + color: #f33; +} +.main h3 { + font-size: 13px; + color: #999; + position: absolute; + left: 0; + top: 33px; +} +@media screen and (max-width: 420px) { + .main h2 { + font-size: 16px; + margin-left: 0; + } + .main h2 a:hover { + color: #f66; + } + .main h3 { + font-size: 11px; + position: static; + margin-bottom: 10px; + } + .main ul li { + padding: 18px 0 20px; + } + .main ul li:first-child { + margin-top: -35px; + } +} diff --git a/examples/async-component-injection/assets/css/post.css b/examples/async-component-injection/assets/css/post.css new file mode 100644 index 0000000000..128b175f70 --- /dev/null +++ b/examples/async-component-injection/assets/css/post.css @@ -0,0 +1,307 @@ +.gutter pre { + color: #999; +} +pre { + color: #525252; +} +pre .function .keyword, +pre .constant { + color: #0092db; +} +pre .keyword, +pre .attribute { + color: #e96900; +} +pre .number, +pre .literal { + color: #ae81ff; +} +pre .tag, +pre .tag .title, +pre .change, +pre .winutils, +pre .flow, +pre .lisp .title, +pre .clojure .built_in, +pre .nginx .title, +pre .tex .special { + color: #2973b7; +} +pre .class .title { + color: #fff; +} +pre .symbol, +pre .symbol .string, +pre .value, +pre .regexp { + color: #42b983; +} +pre .title { + color: #a6e22e; +} +pre .tag .value, +pre .string, +pre .subst, +pre .haskell .type, +pre .preprocessor, +pre .ruby .class .parent, +pre .built_in, +pre .sql .aggregate, +pre .django .template_tag, +pre .django .variable, +pre .smalltalk .class, +pre .javadoc, +pre .django .filter .argument, +pre .smalltalk .localvars, +pre .smalltalk .array, +pre .attr_selector, +pre .pseudo, +pre .addition, +pre .stream, +pre .envvar, +pre .apache .tag, +pre .apache .cbracket, +pre .tex .command, +pre .prompt { + color: #42b983; +} +pre .comment, +pre .java .annotation, +pre .python .decorator, +pre .template_comment, +pre .pi, +pre .doctype, +pre .deletion, +pre .shebang, +pre .apache .sqbracket, +pre .tex .formula { + color: #b3b3b3; +} +pre .coffeescript .javascript, +pre .javascript .xml, +pre .tex .formula, +pre .xml .javascript, +pre .xml .vbscript, +pre .xml .css, +pre .xml .cdata { + opacity: 0.5; +} +.main .post { + position: relative; + padding-bottom: 30px; + margin-bottom: 30px; + border-bottom: 1px solid #e6e6e6; +} +.main .post h1, +.main .post h2 { + text-transform: uppercase; + letter-spacing: 0px; +} +.main .post h1 a:hover, +.main .post h2 a:hover { + border-bottom: 3px solid #666; +} +.main .post h1 { + font-size: 32px; + margin: 0 0 45px; + letter-spacing: 1px; +} +.main .post h2 { + font-size: 24px; + margin: 60px 0 30px; + position: relative; +} +.main .post h2:before { + content: ''; + border-left: 5px solid #41b883; + position: absolute; + left: -15px; + height: 75%; + top: 12%; +} +.main .post h3 { + margin: 30px 0 15px; +} +.main .post .date { + font-family: "Montserrat", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, sans-serif; + font-size: 13px; + color: #999; + margin: 0 0 30px; + letter-spacing: 1px; + position: initial; + text-transform: none; +} +.main .post .content { + text-align: left; + line-height: 1.8em; +} +.main .post .content p, +.main .post .content ul, +.main .post .content ol { + margin: 1em 0 1.5em; +} +.main .post .content strong { + font-weight: 600; + color: #444; +} +.main .post .content ol { + padding-left: 1.6em; +} +.main .post .content ul { + padding-left: 15px; + list-style-type: none; +} +.main .post .content ul li:before { + position: absolute; + font-weight: 600; + content: " ยท "; + margin: 0; + left: 0; +} +.main .post .content a { + color: #41b883; + border-bottom: 2px solid transparent; +} +.main .post .content a:hover { + color: #41b883; + border-bottom-color: #41b883; +} +.main .post .content .highlight, +.main .post .content .highlight table { + margin: 0; + width: 100%; +} +.main .post .content .highlight { + overflow-x: auto; +} +.main .post .content .highlight table, +.main .post .content .highlight tr, +.main .post .content .highlight td { + padding: 0; + border-collapse: collapse; +} +.main .post .content code { + font-family: "Roboto Mono", "Menlo", "Consolas", monospace; + font-size: 13px; + background-color: #f6f6f6; + padding: 3px 10px; + margin: 0 5px; + border-radius: 2px; +} +.main .post .content pre { + font-family: "Roboto Mono", "Menlo", "Consolas", monospace; + font-size: 13px; + overflow-x: auto; + text-align: left; + padding: 15px 25px; + background-color: #f6f6f6; + line-height: 1.5em; +} +.main .post .content .code pre { + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; +} +.main .post .content .gutter pre { + padding: 15px 0 15px 15px; + color: #75715e; + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; +} +.main .post .content blockquote { + margin: 2em 0; + padding-left: 30px; + border-left: 5px solid #e6e6e6; +} +.main .post .content blockquote p { + font-size: 17px; + font-style: italic; + line-height: 1.8em; + color: #999; +} +.main .post img { + display: block; + max-width: 100%; +} +.blog-nav { + position: fixed; + bottom: 20px; + height: 20px; + line-height: 20px; + font-family: "Montserrat", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, sans-serif; + font-size: 15px; + color: #999; + text-decoration: none; + cursor: pointer; + letter-spacing: 1px; + border-bottom: 3px solid transparent; +} +.blog-nav:hover { + color: #333; + border-bottom-color: #333; +} +#newer { + left: 40px; +} +#older { + right: 40px; +} +.show-comments { + font-family: "Montserrat", "Helvetica Neue", "Hiragino Sans GB", "LiHei Pro", Arial, sans-serif; + text-align: center; +} +.show-comments a { + color: #999; + cursor: pointer; +} +.show-comments a:hover { + color: #666; +} +@media screen and (max-width: 900px) { + .main .post { + padding-bottom: 80px; + } + .blog-nav { + position: absolute; + bottom: 30px; + } + #newer { + left: 0; + } + #older { + right: 0; + } +} +@media screen and (max-width: 420px) { + .main { + margin-top: 32px; + } + .main .post h1 { + font-size: 24px; + margin: 0 0 30px; + } + .main .post h2 { + font-size: 20px; + margin: 30px 0 15px; + } + .main .post h3 { + font-size: 16px; + line-height: 1.3em; + } + .main .post .date { + font-size: 12px; + margin: 0 0 20px; + } + .main .post .content { + font-size: 15px; + } + .main .post .content pre { + font-size: 12px; + } + .main .post .content blockquote p { + font-size: 16px; + } + .blog-nav { + font-size: 14px; + color: #444; + } +} diff --git a/examples/async-component-injection/assets/img/github.png b/examples/async-component-injection/assets/img/github.png new file mode 100644 index 0000000000..3844ec302d Binary files /dev/null and b/examples/async-component-injection/assets/img/github.png differ diff --git a/examples/async-component-injection/assets/img/swimmer.jpg b/examples/async-component-injection/assets/img/swimmer.jpg new file mode 100644 index 0000000000..e22693d4e6 Binary files /dev/null and b/examples/async-component-injection/assets/img/swimmer.jpg differ diff --git a/examples/async-component-injection/assets/img/twitter.png b/examples/async-component-injection/assets/img/twitter.png new file mode 100644 index 0000000000..e84e1b8a28 Binary files /dev/null and b/examples/async-component-injection/assets/img/twitter.png differ diff --git a/examples/async-component-injection/layouts/default.vue b/examples/async-component-injection/layouts/default.vue new file mode 100644 index 0000000000..204ba8cf6c --- /dev/null +++ b/examples/async-component-injection/layouts/default.vue @@ -0,0 +1,15 @@ + + diff --git a/examples/async-component-injection/nuxt.config.js b/examples/async-component-injection/nuxt.config.js new file mode 100644 index 0000000000..569cee3b0b --- /dev/null +++ b/examples/async-component-injection/nuxt.config.js @@ -0,0 +1,22 @@ +module.exports = { + head: { + title: 'Nuxt Blog', + meta: [ + { charset: 'utf-8' }, + { name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' } + ], + link: [ + { rel: 'icon', href: '/favicon.ico', type: 'image/x-icon' }, + { rel: 'stylesheet', href: 'http://fonts.googleapis.com/css?family=Montserrat|Roboto:400,400italic,600|Roboto+Mono', type: 'text/css' } + ] + }, + css: [ + '@/assets/css/common.css' + ], + generate: { + routes: [ + '/deep-dive-into-ocean', + '/welcome-to-my-blog' + ] + } +} diff --git a/examples/async-component-injection/package.json b/examples/async-component-injection/package.json new file mode 100644 index 0000000000..4aeda9d845 --- /dev/null +++ b/examples/async-component-injection/package.json @@ -0,0 +1,11 @@ +{ + "name": "components-injection-nuxt", + "dependencies": { + "nuxt": "latest" + }, + "scripts": { + "dev": "nuxt", + "build": "nuxt build", + "start": "nuxt" + } +} diff --git a/examples/async-component-injection/pages/_slug.vue b/examples/async-component-injection/pages/_slug.vue new file mode 100644 index 0000000000..32359858fb --- /dev/null +++ b/examples/async-component-injection/pages/_slug.vue @@ -0,0 +1,21 @@ + + + + + \ No newline at end of file diff --git a/examples/dynamic-components/components/image.vue b/examples/dynamic-components/components/image.vue new file mode 100644 index 0000000000..3a083bf8d8 --- /dev/null +++ b/examples/dynamic-components/components/image.vue @@ -0,0 +1,38 @@ + + + + + \ No newline at end of file diff --git a/examples/dynamic-components/components/text.vue b/examples/dynamic-components/components/text.vue new file mode 100644 index 0000000000..856ed0f78c --- /dev/null +++ b/examples/dynamic-components/components/text.vue @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/examples/dynamic-components/js/messages.js b/examples/dynamic-components/js/messages.js new file mode 100644 index 0000000000..a778e2ce35 --- /dev/null +++ b/examples/dynamic-components/js/messages.js @@ -0,0 +1,27 @@ +export const messages = [ + { component: 'vText', data: 'Welcome to the Dynamic Component demo!' }, + { component: 'vImage', data: 'https://placeimg.com/350/200/animals' }, + { component: 'vCode', data: 'var a = 1;\nvar b = 2;\nb = a;' }, + { + component: 'vChart', + data: { + labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + datasets: [ + { + label: 'Activity', + backgroundColor: '#41b883', + data: [40, 20, 12, 39, 10, 40, 39, 50, 40, 20, 12, 11] + } + ] + } + }, + { component: 'vText', data: 'End of demo ๐ŸŽ‰' } +] + +async function streamMessages(fn, i = 0) { + if (i >= messages.length) return + await fn(messages[i]) + setTimeout(() => streamMessages(fn, i + 1), 1500) +} + +export default streamMessages diff --git a/examples/dynamic-components/nuxt.config.js b/examples/dynamic-components/nuxt.config.js new file mode 100644 index 0000000000..4cd058a6a5 --- /dev/null +++ b/examples/dynamic-components/nuxt.config.js @@ -0,0 +1,9 @@ +module.exports = { + head: { + titleTemplate: 'Nuxt.js - Dynamic Components', + meta: [ + { charset: 'utf-8' }, + { name: 'viewport', content: 'width=device-width, initial-scale=1' } + ] + } +} diff --git a/examples/dynamic-components/package.json b/examples/dynamic-components/package.json new file mode 100644 index 0000000000..7486edc9cd --- /dev/null +++ b/examples/dynamic-components/package.json @@ -0,0 +1,14 @@ +{ + "name": "dynamic-components-nuxt", + "dependencies": { + "chart.js": "^2.7.0", + "nuxt": "latest", + "vue-chartjs": "^2.8.7" + }, + "scripts": { + "dev": "nuxt", + "build": "nuxt build", + "start": "nuxt start", + "generate": "nuxt generate" + } +} diff --git a/examples/dynamic-components/pages/index.vue b/examples/dynamic-components/pages/index.vue new file mode 100755 index 0000000000..ed7ce9e045 --- /dev/null +++ b/examples/dynamic-components/pages/index.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/examples/dynamic-components/pages/ssr.vue b/examples/dynamic-components/pages/ssr.vue new file mode 100644 index 0000000000..0ada8273d0 --- /dev/null +++ b/examples/dynamic-components/pages/ssr.vue @@ -0,0 +1,63 @@ + + + + + \ No newline at end of file diff --git a/examples/dynamic-layouts/pages/about.vue b/examples/dynamic-layouts/pages/about.vue index 8ed01c381f..18d0789771 100644 --- a/examples/dynamic-layouts/pages/about.vue +++ b/examples/dynamic-layouts/pages/about.vue @@ -8,7 +8,7 @@ diff --git a/examples/global-css/assets/roboto.woff2 b/examples/global-css/assets/roboto.woff2 new file mode 100644 index 0000000000..555f98b120 Binary files /dev/null and b/examples/global-css/assets/roboto.woff2 differ diff --git a/examples/global-css/css/main.css b/examples/global-css/css/main.css index 762c321cb7..debffff97e 100644 --- a/examples/global-css/css/main.css +++ b/examples/global-css/css/main.css @@ -1,3 +1,11 @@ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + src: local('Roboto'), local('Roboto-Regular'), url(../assets/roboto.woff2) format('woff2'); + unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F; +} + body { background: #eee; text-align: center; @@ -7,6 +15,7 @@ body { position: absolute; top: 0; left: 0; + font-family: 'Roboto'; } .content { margin-top: 100px; diff --git a/examples/global-css/nuxt.config.js b/examples/global-css/nuxt.config.js index f5ae0a61d1..3dc2839759 100644 --- a/examples/global-css/nuxt.config.js +++ b/examples/global-css/nuxt.config.js @@ -1,12 +1,20 @@ -const { join } = require('path') - module.exports = { + head: { + meta: [ + { charset: 'utf-8' }, + { name: 'viewport', content: 'width=device-width, initial-scale=1' }, + { hid: 'description', name: 'description', content: 'Meta description' } + ] + }, css: [ - 'hover.css/css/hover-min.css', - 'bulma/bulma.sass', - join(__dirname, 'css/main.css') + 'bulma/css/bulma.css', + '~/css/main.css' ], - build: { - extractCSS: true + render: { + bundleRenderer: { + shouldPreload: (file, type) => { + return ['script', 'style', 'font'].includes(type) + } + } } } diff --git a/examples/global-css/package.json b/examples/global-css/package.json index 9a60d27928..1525d69e4e 100644 --- a/examples/global-css/package.json +++ b/examples/global-css/package.json @@ -1,11 +1,8 @@ { "name": "nuxt-global-css", "dependencies": { - "bulma": "^0.4.0", - "hover.css": "^2.2.0", - "node-sass": "^4.5.1", - "nuxt": "^0.10.0", - "sass-loader": "^6.0.3" + "bulma": "^0.5.1", + "nuxt": "latest" }, "scripts": { "dev": "nuxt", diff --git a/examples/head-elements/README.md b/examples/head-elements/README.md deleted file mode 100644 index 1b7d3962d2..0000000000 --- a/examples/head-elements/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Updating headers with Nuxt.js - -https://nuxtjs.org/examples/seo-html-head diff --git a/examples/hello-world-jsx/pages/about.vue b/examples/hello-world-jsx/pages/about.vue index 7f7b2af070..26ed2649b8 100644 --- a/examples/hello-world-jsx/pages/about.vue +++ b/examples/hello-world-jsx/pages/about.vue @@ -1,11 +1,11 @@ diff --git a/examples/hello-world/package.json b/examples/hello-world/package.json index 87f109c3f9..2e16d42040 100644 --- a/examples/hello-world/package.json +++ b/examples/hello-world/package.json @@ -4,6 +4,8 @@ "nuxt": "latest" }, "scripts": { - "start": "nuxt" + "dev": "nuxt", + "build": "nuxt build", + "start": "nuxt start" } } diff --git a/examples/hello-world/pages/about.vue b/examples/hello-world/pages/about.vue index becdb2f20a..88830a4df4 100755 --- a/examples/hello-world/pages/about.vue +++ b/examples/hello-world/pages/about.vue @@ -7,9 +7,9 @@ diff --git a/examples/i18n/pages/index.vue b/examples/i18n/pages/index.vue index fab3df9c9c..f0ca25b6f8 100644 --- a/examples/i18n/pages/index.vue +++ b/examples/i18n/pages/index.vue @@ -1,4 +1,4 @@ diff --git a/examples/i18n/store/index.js b/examples/i18n/store/index.js index 0c12e69efa..cd8f00517a 100644 --- a/examples/i18n/store/index.js +++ b/examples/i18n/store/index.js @@ -4,7 +4,7 @@ export const state = () => ({ }) export const mutations = { - SET_LANG (state, locale) { + SET_LANG(state, locale) { if (state.locales.indexOf(locale) !== -1) { state.locale = locale } diff --git a/examples/layout-transitions/README.md b/examples/layout-transitions/README.md new file mode 100644 index 0000000000..3bdd2a9092 --- /dev/null +++ b/examples/layout-transitions/README.md @@ -0,0 +1,3 @@ +# Layout transitions with Nuxt.js + +https://nuxtjs.org/examples/layout-transitions diff --git a/examples/layout-transitions/assets/main.css b/examples/layout-transitions/assets/main.css new file mode 100644 index 0000000000..63b42c3f6f --- /dev/null +++ b/examples/layout-transitions/assets/main.css @@ -0,0 +1,52 @@ +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; +} + +.container { + text-align: center; + padding-top: 200px; + font-size: 20px; + transition: all .5s cubic-bezier(.55,0,.1,1); +} + +.page-enter-active, .page-leave-active { + transition: opacity .5s +} +.page-enter, .page-leave-active { + opacity: 0 +} + +.layout-enter-active, .layout-leave-active { + transition: opacity 0.5s +} +.layout-enter, .layout-leave-active { + opacity: 0 +} + +.bounce-enter-active { + animation: bounce-in .5s; +} +.bounce-leave-active { + animation: bounce-out .5s; +} +@keyframes bounce-in { + 0% { transform: scale(0) } + 50% { transform: scale(1.5) } + 100% { transform: scale(1) } +} +@keyframes bounce-out { + 0% { transform: scale(1) } + 50% { transform: scale(1.5) } + 100% { transform: scale(0) } +} + +.slide-left-enter, +.slide-right-leave-active { + opacity: 0; + transform: translate(30px, 0); +} +.slide-left-leave-active, +.slide-right-enter { + opacity: 0; + transform: translate(-30px, 0); +} diff --git a/examples/layout-transitions/layouts/secondary.vue b/examples/layout-transitions/layouts/secondary.vue new file mode 100644 index 0000000000..57a89e59ee --- /dev/null +++ b/examples/layout-transitions/layouts/secondary.vue @@ -0,0 +1,6 @@ + diff --git a/examples/layout-transitions/nuxt.config.js b/examples/layout-transitions/nuxt.config.js new file mode 100644 index 0000000000..e1b2d64a6c --- /dev/null +++ b/examples/layout-transitions/nuxt.config.js @@ -0,0 +1,10 @@ +module.exports = { + build: { + vendor: ['axios'] + }, + css: ['~/assets/main.css'], + layoutTransition: { + name: 'layout', + mode: 'out-in' + } +} diff --git a/examples/layout-transitions/package.json b/examples/layout-transitions/package.json new file mode 100644 index 0000000000..822831ec96 --- /dev/null +++ b/examples/layout-transitions/package.json @@ -0,0 +1,12 @@ +{ + "name": "nuxt-layout-transitions", + "dependencies": { + "axios": "^0.15.3", + "nuxt": "latest" + }, + "scripts": { + "dev": "nuxt", + "build": "nuxt build", + "start": "nuxt start" + } +} diff --git a/examples/layout-transitions/pages/about.vue b/examples/layout-transitions/pages/about.vue new file mode 100644 index 0000000000..0ec094454f --- /dev/null +++ b/examples/layout-transitions/pages/about.vue @@ -0,0 +1,13 @@ + + + diff --git a/examples/layout-transitions/pages/index.vue b/examples/layout-transitions/pages/index.vue new file mode 100644 index 0000000000..9b8b4fefa5 --- /dev/null +++ b/examples/layout-transitions/pages/index.vue @@ -0,0 +1,8 @@ + diff --git a/examples/layout-transitions/pages/users-2.vue b/examples/layout-transitions/pages/users-2.vue new file mode 100644 index 0000000000..e1a038e0f1 --- /dev/null +++ b/examples/layout-transitions/pages/users-2.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/examples/layout-transitions/pages/users.vue b/examples/layout-transitions/pages/users.vue new file mode 100644 index 0000000000..f7399382cf --- /dev/null +++ b/examples/layout-transitions/pages/users.vue @@ -0,0 +1,76 @@ + + + + + diff --git a/examples/markdownit/README.md b/examples/markdownit/README.md new file mode 100644 index 0000000000..a287cad252 --- /dev/null +++ b/examples/markdownit/README.md @@ -0,0 +1,5 @@ +# Markdown Example + +> Convert Markdown file to HTML using markdown-it. + +**See [Markdownit Module](https://github.com/nuxt-community/modules/tree/master/packages/markdownit) for easy integration with [Nuxt.js](https://nuxtjs.org).** diff --git a/examples/markdownit/nuxt.config.js b/examples/markdownit/nuxt.config.js new file mode 100644 index 0000000000..5660fb118d --- /dev/null +++ b/examples/markdownit/nuxt.config.js @@ -0,0 +1,16 @@ +module.exports = { + modules: [ + '@nuxtjs/markdownit' + ] + // [optional] markdownit options + // See https://github.com/markdown-it/markdown-it + // markdownit: { + // preset: 'default', + // linkify: true, + // breaks: true, + // use: [ + // 'markdown-it-container', + // 'markdown-it-attrs' + // ] + // } +} diff --git a/examples/markdownit/package.json b/examples/markdownit/package.json new file mode 100644 index 0000000000..76056fdcbb --- /dev/null +++ b/examples/markdownit/package.json @@ -0,0 +1,14 @@ +{ + "name": "nuxt-markdownit", + "version": "1.0.0", + "dependencies": { + "@nuxtjs/markdownit": "^1.1.2", + "nuxt": "latest", + "pug": "^2.0.0-rc.4" + }, + "scripts": { + "dev": "nuxt", + "build": "nuxt build", + "start": "nuxt start" + } +} diff --git a/examples/markdownit/pages/about.vue b/examples/markdownit/pages/about.vue new file mode 100644 index 0000000000..aa487b80bb --- /dev/null +++ b/examples/markdownit/pages/about.vue @@ -0,0 +1,6 @@ + diff --git a/examples/markdownit/pages/index.vue b/examples/markdownit/pages/index.vue new file mode 100644 index 0000000000..41b534ef46 --- /dev/null +++ b/examples/markdownit/pages/index.vue @@ -0,0 +1,21 @@ + + + diff --git a/examples/markdownit/pages/pug.vue b/examples/markdownit/pages/pug.vue new file mode 100644 index 0000000000..e15883bc67 --- /dev/null +++ b/examples/markdownit/pages/pug.vue @@ -0,0 +1,18 @@ + + + diff --git a/examples/meta-info/README.md b/examples/meta-info/README.md new file mode 100644 index 0000000000..9832e65a42 --- /dev/null +++ b/examples/meta-info/README.md @@ -0,0 +1,13 @@ +# Manage your app's meta information + +Nuxt.js uses [vue-meta](https://github.com/declandewet/vue-meta) to manage page meta info (such as: meta, title, link, style, script) of your application. + +## Example + +SEO: https://nuxtjs.org/examples/seo-html-head + +## Documentation + +Nuxt.js: https://nuxtjs.org/guide/views#html-head + +vue-meta: https://github.com/declandewet/vue-meta#table-of-contents diff --git a/examples/head-elements/components/twitter-head-card.vue b/examples/meta-info/components/twitter-head-card.vue similarity index 100% rename from examples/head-elements/components/twitter-head-card.vue rename to examples/meta-info/components/twitter-head-card.vue diff --git a/examples/head-elements/nuxt.config.js b/examples/meta-info/nuxt.config.js similarity index 100% rename from examples/head-elements/nuxt.config.js rename to examples/meta-info/nuxt.config.js diff --git a/examples/head-elements/package.json b/examples/meta-info/package.json similarity index 100% rename from examples/head-elements/package.json rename to examples/meta-info/package.json diff --git a/examples/head-elements/pages/about.vue b/examples/meta-info/pages/about.vue similarity index 90% rename from examples/head-elements/pages/about.vue rename to examples/meta-info/pages/about.vue index 4118718f1d..745878904f 100644 --- a/examples/head-elements/pages/about.vue +++ b/examples/meta-info/pages/about.vue @@ -8,7 +8,7 @@ diff --git a/examples/offline-nuxt/nuxt.config.js b/examples/offline-nuxt/nuxt.config.js deleted file mode 100644 index 915a70f372..0000000000 --- a/examples/offline-nuxt/nuxt.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - offline: true, // true or https://github.com/NekR/offline-plugin/blob/master/docs/options.md - plugins: [ - { src: '~plugins/offline.js', ssr: false } - ] -} diff --git a/examples/offline-nuxt/pages/index.vue b/examples/offline-nuxt/pages/index.vue deleted file mode 100644 index 5846d84ea3..0000000000 --- a/examples/offline-nuxt/pages/index.vue +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/examples/offline-nuxt/plugins/offline.js b/examples/offline-nuxt/plugins/offline.js deleted file mode 100644 index 7224e02c00..0000000000 --- a/examples/offline-nuxt/plugins/offline.js +++ /dev/null @@ -1,19 +0,0 @@ -if (process.env.NODE_ENV === 'production') { - var OfflinePlugin = require('offline-plugin/runtime') - window.onNuxtReady(() => { - OfflinePlugin.install({ - onInstalled: function () { - console.log('Offline plugin installed.') // eslint-disable-line no-console - }, - onUpdating: function () { - - }, - onUpdateReady: function () { - OfflinePlugin.applyUpdate() - }, - onUpdated: function () { - window.location.reload() - } - }) - }) -} diff --git a/examples/plugins-vendor/nuxt.config.js b/examples/plugins-vendor/nuxt.config.js index 76d693a3a1..f6aac43a42 100644 --- a/examples/plugins-vendor/nuxt.config.js +++ b/examples/plugins-vendor/nuxt.config.js @@ -4,6 +4,6 @@ module.exports = { }, plugins: [ // ssr: false to only include it on client-side - { src: '~plugins/vue-notifications.js', ssr: false } + { src: '~/plugins/vue-notifications.js', ssr: false } ] } diff --git a/examples/plugins-vendor/package.json b/examples/plugins-vendor/package.json index c0bd19a84f..995af2c330 100644 --- a/examples/plugins-vendor/package.json +++ b/examples/plugins-vendor/package.json @@ -1,10 +1,10 @@ { "name": "nuxt-plugins-vendor", "dependencies": { - "axios": "^0.15.2", - "mini-toastr": "^0.3.10", + "axios": "^0.16.2", + "mini-toastr": "^0.6.5", "nuxt": "latest", - "vue-notifications": "^0.7.0" + "vue-notifications": "^0.8.0" }, "scripts": { "dev": "nuxt", diff --git a/examples/plugins-vendor/pages/about.vue b/examples/plugins-vendor/pages/about.vue index 1567978005..8283527039 100644 --- a/examples/plugins-vendor/pages/about.vue +++ b/examples/plugins-vendor/pages/about.vue @@ -9,8 +9,9 @@ import axios from 'axios' export default { - asyncData () { - return axios.get('https://jsonplaceholder.typicode.com/photos/4').then(res => res.data) + asyncData() { + const nb = Math.max(1, Math.round(Math.random() * 10)) + return axios.get(`https://jsonplaceholder.typicode.com/photos/${nb}`).then(res => res.data) } } diff --git a/examples/plugins-vendor/pages/index.vue b/examples/plugins-vendor/pages/index.vue index 4113897d1b..3d915f9a5f 100644 --- a/examples/plugins-vendor/pages/index.vue +++ b/examples/plugins-vendor/pages/index.vue @@ -12,7 +12,7 @@ if (process.browser) { } export default { - mounted () { + mounted() { miniToastr.init() }, notifications: { diff --git a/examples/routes-transitions/nuxt.config.js b/examples/routes-transitions/nuxt.config.js index b904282bbc..c4264e9c56 100644 --- a/examples/routes-transitions/nuxt.config.js +++ b/examples/routes-transitions/nuxt.config.js @@ -2,6 +2,5 @@ module.exports = { build: { vendor: ['axios'] }, - css: ['assets/main.css'], - loading: false + css: ['~/assets/main.css'] } diff --git a/examples/routes-transitions/pages/users.vue b/examples/routes-transitions/pages/users.vue index ecf89457ce..a02582e753 100644 --- a/examples/routes-transitions/pages/users.vue +++ b/examples/routes-transitions/pages/users.vue @@ -1,5 +1,5 @@ // **PLEASE NOTE** All "Nuxt Class Components" require at minimum a script tag that exports a default object \ No newline at end of file + diff --git a/examples/typescript/store/index.ts b/examples/typescript/store/index.ts index 292c381318..bfffcd5175 100644 --- a/examples/typescript/store/index.ts +++ b/examples/typescript/store/index.ts @@ -1,33 +1,33 @@ -import axios from "~plugins/axios"; - -export const state = () => ({ - selected: 1, - people: [] -}); - -export const mutations = { - select(state, id) { - state.selected = id; - }, - setPeople(state, people) { - state.people = people; - } -}; - -export const getters = { - selectedPerson: state => { - const p = state.people.find(person => person.id === state.selected); - return p ? p : { first_name: "Please,", last_name: "select someone" }; - } -}; - -export const actions = { - async nuxtServerInit({ commit }) { - const response = await axios.get("/random-data.json"); - const people = response.data.slice(0, 10); - commit("setPeople", people); - }, - select({ commit }, id) { - commit("select", id); - } -}; +import axios from "~/plugins/axios"; + +export const state = () => ({ + selected: 1, + people: [] +}); + +export const mutations = { + select(state, id) { + state.selected = id; + }, + setPeople(state, people) { + state.people = people; + } +}; + +export const getters = { + selectedPerson: state => { + const p = state.people.find(person => person.id === state.selected); + return p ? p : { first_name: "Please,", last_name: "select someone" }; + } +}; + +export const actions = { + async nuxtServerInit({ commit }) { + const response = await axios.get("/random-data.json"); + const people = response.data.slice(0, 10); + commit("setPeople", people); + }, + select({ commit }, id) { + commit("select", id); + } +}; diff --git a/examples/typescript/tsconfig.json b/examples/typescript/tsconfig.json index e2812a86cd..557fd275fa 100644 --- a/examples/typescript/tsconfig.json +++ b/examples/typescript/tsconfig.json @@ -1,31 +1,29 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": [ - "dom", - "es2015" - ], - "module": "es2015", - "moduleResolution": "node", - "experimentalDecorators": true, - "declaration": true, - "noImplicitAny": false, - "noImplicitThis": false, - "strictNullChecks": true, - "removeComments": true, - "suppressImplicitAnyIndexErrors": true, - "allowSyntheticDefaultImports": true, - "baseUrl": ".", - "paths": { - "~": ["./"], - "~assets/*": ["./assets/*"], - "~components/*": ["./components/*"], - "~middleware/*": ["./middleware/*"], - "~pages/*": ["./pages/*"], - "~plugins/*": ["./plugins/*"], - "~static/*": ["./static/*"], - "~store": ["./.nuxt/store"], - "~router": ["./.nuxt/router"] - } - } -} +{ + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "es2015" + ], + "module": "es2015", + "moduleResolution": "node", + "experimentalDecorators": true, + "noImplicitAny": false, + "noImplicitThis": false, + "strictNullChecks": true, + "removeComments": true, + "suppressImplicitAnyIndexErrors": true, + "allowSyntheticDefaultImports": true, + "baseUrl": ".", + "allowJs": true, + "paths": { + "~/": ["./"], + "~/assets/*": ["./assets/*"], + "~/components/*": ["./components/*"], + "~/middleware/*": ["./middleware/*"], + "~/pages/*": ["./pages/*"], + "~/plugins/*": ["./plugins/*"], + "~/static/*": ["./static/*"] + } + } +} diff --git a/examples/uikit/README.md b/examples/uikit/README.md new file mode 100644 index 0000000000..b24165bb26 --- /dev/null +++ b/examples/uikit/README.md @@ -0,0 +1,7 @@ +# UIKit with Nuxt.js + +UIkit: https://github.com/uikit/uikit + +Live demo: https://uikit.nuxtjs.org + +Live edit: https://glitch.com/edit/#!/nuxt-uitkit diff --git a/examples/uikit/layouts/default.vue b/examples/uikit/layouts/default.vue new file mode 100644 index 0000000000..a2a570ace7 --- /dev/null +++ b/examples/uikit/layouts/default.vue @@ -0,0 +1,5 @@ + diff --git a/examples/uikit/nuxt.config.js b/examples/uikit/nuxt.config.js new file mode 100644 index 0000000000..6f916f758e --- /dev/null +++ b/examples/uikit/nuxt.config.js @@ -0,0 +1,6 @@ +module.exports = { + css: ['uikit/dist/css/uikit.css'], + plugins: [ + { src: '~/plugins/uikit.js', ssr: false } + ] +} diff --git a/examples/uikit/package.json b/examples/uikit/package.json new file mode 100644 index 0000000000..bc539ba586 --- /dev/null +++ b/examples/uikit/package.json @@ -0,0 +1,13 @@ +{ + "name": "uikit-nuxt", + "dependencies": { + "nuxt": "latest", + "uikit": "^3.0.0-beta.30", + "jquery": "^3.2.1" + }, + "scripts": { + "dev": "nuxt", + "build": "nuxt build", + "start": "nuxt start" + } +} diff --git a/examples/uikit/pages/about.vue b/examples/uikit/pages/about.vue new file mode 100644 index 0000000000..67de726940 --- /dev/null +++ b/examples/uikit/pages/about.vue @@ -0,0 +1,19 @@ + + + diff --git a/examples/uikit/pages/index.vue b/examples/uikit/pages/index.vue new file mode 100644 index 0000000000..ab5b67b26a --- /dev/null +++ b/examples/uikit/pages/index.vue @@ -0,0 +1,12 @@ + diff --git a/examples/uikit/plugins/uikit.js b/examples/uikit/plugins/uikit.js new file mode 100644 index 0000000000..81a92335a1 --- /dev/null +++ b/examples/uikit/plugins/uikit.js @@ -0,0 +1,5 @@ +import UIkit from 'uikit' +import Icons from 'uikit/dist/js/uikit-icons' + +// loads the Icon plugin +UIkit.use(Icons) diff --git a/examples/vue-apollo/README.md b/examples/vue-apollo/README.md index 8458ecb2d5..92ef54ec86 100644 --- a/examples/vue-apollo/README.md +++ b/examples/vue-apollo/README.md @@ -1,7 +1,7 @@ -# WIP - # Vue-Apollo with Nuxt.js -https://nuxtjs.org/examples/vue-apollo +Demo: https://nuxt-vue-apollo.now.sh/ + +https://github.com/nuxt-community/apollo-module https://github.com/Akryum/vue-apollo diff --git a/examples/vue-apollo/apollo/network-interfaces/default.js b/examples/vue-apollo/apollo/network-interfaces/default.js new file mode 100644 index 0000000000..7b6d3f6c14 --- /dev/null +++ b/examples/vue-apollo/apollo/network-interfaces/default.js @@ -0,0 +1,5 @@ +import { createNetworkInterface } from 'apollo-client' + +export default (ctx) => { + return createNetworkInterface({ uri: 'https://api.graph.cool/simple/v1/cj1dqiyvqqnmj0113yuqamkuu' }) +} diff --git a/examples/vue-apollo/apollo/queries/allCars.gql b/examples/vue-apollo/apollo/queries/allCars.gql new file mode 100644 index 0000000000..81c1448c14 --- /dev/null +++ b/examples/vue-apollo/apollo/queries/allCars.gql @@ -0,0 +1,8 @@ +{ + allCars { + id + make + model + year + } +} diff --git a/examples/vue-apollo/apollo/queries/car.gql b/examples/vue-apollo/apollo/queries/car.gql new file mode 100644 index 0000000000..c380d25884 --- /dev/null +++ b/examples/vue-apollo/apollo/queries/car.gql @@ -0,0 +1,8 @@ +query Car($id: ID!) { + Car(id: $id) { + make + model + photoURL + price + } +} diff --git a/examples/vue-apollo/middleware/apollo.js b/examples/vue-apollo/middleware/apollo.js deleted file mode 100644 index 7b079c92f1..0000000000 --- a/examples/vue-apollo/middleware/apollo.js +++ /dev/null @@ -1,7 +0,0 @@ -export default async function ({ isServer, apolloProvider }) { - if (isServer) { - const ensureReady = apolloProvider.collect() - console.log('Call ensureReady!', ensureReady()) - await ensureReady() - } -} diff --git a/examples/vue-apollo/nuxt.config.js b/examples/vue-apollo/nuxt.config.js index f73ea6eaf6..bdf96f2a93 100644 --- a/examples/vue-apollo/nuxt.config.js +++ b/examples/vue-apollo/nuxt.config.js @@ -1,12 +1,8 @@ module.exports = { - build: { - vendor: ['vue-apollo', 'apollo-client'] - }, - router: { - middleware: 'apollo' - }, - plugins: [ - // Will inject the plugin in the $root app and also in the context as `apolloProvider` - { src: '~plugins/apollo.js', injectAs: 'apolloProvider' } - ] + modules: ['@nuxtjs/apollo'], + apollo: { + networkInterfaces: { + default: '~/apollo/network-interfaces/default.js' + } + } } diff --git a/examples/vue-apollo/package.json b/examples/vue-apollo/package.json index 2f823a1e8e..c9ffee9f88 100644 --- a/examples/vue-apollo/package.json +++ b/examples/vue-apollo/package.json @@ -1,13 +1,13 @@ { - "name": "nuxt-i18n", + "name": "nuxt-vue-apollo", "dependencies": { - "apollo-client": "^1.0.3", - "nuxt": "latest", - "vue-apollo": "^2.1.0-beta.2" + "@nuxtjs/apollo": "^2.1.1", + "nuxt": "latest" }, "scripts": { - "dev": "node server.js", + "dev": "nuxt", "build": "nuxt build", - "start": "cross-env NODE_ENV=production node server.js" + "start": "nuxt start", + "generate": "nuxt generate" } } diff --git a/examples/vue-apollo/pages/car/_id.vue b/examples/vue-apollo/pages/car/_id.vue new file mode 100644 index 0000000000..b64cfe26f4 --- /dev/null +++ b/examples/vue-apollo/pages/car/_id.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/examples/vue-apollo/pages/index.vue b/examples/vue-apollo/pages/index.vue index bdb630f4ff..5aa6f9a5cd 100644 --- a/examples/vue-apollo/pages/index.vue +++ b/examples/vue-apollo/pages/index.vue @@ -12,21 +12,17 @@ diff --git a/examples/vue-apollo/plugins/apollo.js b/examples/vue-apollo/plugins/apollo.js deleted file mode 100644 index cf888008c9..0000000000 --- a/examples/vue-apollo/plugins/apollo.js +++ /dev/null @@ -1,21 +0,0 @@ -import 'babel-polyfill' -import Vue from 'vue' -import VueApollo from 'vue-apollo' -import { ApolloClient, createNetworkInterface } from 'apollo-client' - -Vue.use(VueApollo) - -const API_ENDPOINT = 'https://api.graph.cool/simple/v1/cj1dqiyvqqnmj0113yuqamkuu' - -const apolloClient = new ApolloClient({ - networkInterface: createNetworkInterface({ - uri: API_ENDPOINT, - transportBatching: true - }) -}) - -const apolloProvider = new VueApollo({ - defaultClient: apolloClient -}) - -export default apolloProvider diff --git a/examples/vue-apollo/server.js b/examples/vue-apollo/server.js deleted file mode 100644 index 32364be29a..0000000000 --- a/examples/vue-apollo/server.js +++ /dev/null @@ -1,27 +0,0 @@ -const Nuxt = require('../../') -const app = require('express')() -const host = process.env.HOST || '127.0.0.1' -const port = process.env.PORT || 3000 - -global.fetch = require('node-fetch') - -// Import and Set Nuxt.js options -let config = require('./nuxt.config.js') -config.dev = !(process.env.NODE_ENV === 'production') - -// Init Nuxt.js -const nuxt = new Nuxt(config) -app.use(nuxt.render) - -// Build only in dev mode -if (config.dev) { - nuxt.build() - .catch((error) => { - console.error(error) // eslint-disable-line no-console - process.exit(1) - }) -} - -// Listen the server -app.listen(port, host) -console.log('Server listening on ' + host + ':' + port) // eslint-disable-line no-console diff --git a/examples/vue-chartjs/README.md b/examples/vue-chartjs/README.md new file mode 100644 index 0000000000..ab534cf92a --- /dev/null +++ b/examples/vue-chartjs/README.md @@ -0,0 +1,3 @@ +# Vue-ChartJS with Nuxt.js + +https://nuxtjs.org/examples diff --git a/examples/vue-chartjs/components/bar-chart.js b/examples/vue-chartjs/components/bar-chart.js new file mode 100644 index 0000000000..f6440d4b2f --- /dev/null +++ b/examples/vue-chartjs/components/bar-chart.js @@ -0,0 +1,8 @@ +import { Bar } from 'vue-chartjs' + +export default Bar.extend({ + props: ['data', 'options'], + mounted() { + this.renderChart(this.data, this.options) + } +}) diff --git a/examples/vue-chartjs/components/doughnut-chart.js b/examples/vue-chartjs/components/doughnut-chart.js new file mode 100644 index 0000000000..f972ab4dc9 --- /dev/null +++ b/examples/vue-chartjs/components/doughnut-chart.js @@ -0,0 +1,8 @@ +import { Doughnut } from 'vue-chartjs' + +export default Doughnut.extend({ + props: ['data', 'options'], + mounted() { + this.renderChart(this.data, this.options) + } +}) diff --git a/examples/vue-chartjs/layouts/default.vue b/examples/vue-chartjs/layouts/default.vue new file mode 100644 index 0000000000..5c9abe4438 --- /dev/null +++ b/examples/vue-chartjs/layouts/default.vue @@ -0,0 +1,36 @@ + + + diff --git a/examples/vue-chartjs/nuxt.config.js b/examples/vue-chartjs/nuxt.config.js new file mode 100644 index 0000000000..e6b67f1882 --- /dev/null +++ b/examples/vue-chartjs/nuxt.config.js @@ -0,0 +1,15 @@ +module.exports = { + head: { + title: 'Nuxt.js + Vue-ChartJS', + meta: [ + { charset: 'utf-8' }, + { name: 'viewport', content: 'width=device-width, initial-scale=1' } + ] + }, + build: { + vendor: ['axios', 'moment', 'chart.js', 'vue-chartjs'] + }, + env: { + githubToken: '42cdf9fd55abf41d24f34c0f8a4d9ada5f9e9b93' + } +} diff --git a/examples/vue-chartjs/package.json b/examples/vue-chartjs/package.json new file mode 100644 index 0000000000..e3217175f5 --- /dev/null +++ b/examples/vue-chartjs/package.json @@ -0,0 +1,15 @@ +{ + "name": "vue-chartjs-nuxt", + "dependencies": { + "axios": "^0.16.2", + "chart.js": "^2.6.0", + "moment": "^2.18.1", + "nuxt": "latest", + "vue-chartjs": "^2.8.2" + }, + "scripts": { + "dev": "nuxt", + "build": "nuxt build", + "start": "nuxt start" + } +} diff --git a/examples/vue-chartjs/pages/contributors.vue b/examples/vue-chartjs/pages/contributors.vue new file mode 100644 index 0000000000..0d1a0649f8 --- /dev/null +++ b/examples/vue-chartjs/pages/contributors.vue @@ -0,0 +1,50 @@ + + + + + diff --git a/examples/vue-chartjs/pages/index.vue b/examples/vue-chartjs/pages/index.vue new file mode 100755 index 0000000000..d1d380470f --- /dev/null +++ b/examples/vue-chartjs/pages/index.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/examples/vue-class-component/components/Base.vue b/examples/vue-class-component/components/Base.vue new file mode 100644 index 0000000000..ed43328dda --- /dev/null +++ b/examples/vue-class-component/components/Base.vue @@ -0,0 +1,40 @@ + + + diff --git a/examples/vue-class-component/components/Child.vue b/examples/vue-class-component/components/Child.vue new file mode 100644 index 0000000000..8063fc7bc4 --- /dev/null +++ b/examples/vue-class-component/components/Child.vue @@ -0,0 +1,23 @@ + + + diff --git a/examples/vue-class-component/nuxt.config.js b/examples/vue-class-component/nuxt.config.js index a61292b4ae..4d32a9ca0a 100644 --- a/examples/vue-class-component/nuxt.config.js +++ b/examples/vue-class-component/nuxt.config.js @@ -2,9 +2,6 @@ module.exports = { build: { babel: { plugins: ['transform-decorators-legacy', 'transform-class-properties'] - }, - extend (config) { - config.resolve.alias['nuxt-class-component'] = '~plugins/nuxt-class-component' } } } diff --git a/examples/vue-class-component/package.json b/examples/vue-class-component/package.json index e672615065..797a48f36e 100644 --- a/examples/vue-class-component/package.json +++ b/examples/vue-class-component/package.json @@ -4,7 +4,7 @@ "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-decorators-legacy": "^1.3.4", "nuxt": "latest", - "vue-class-component": "^5.0.1" + "nuxt-class-component": "^1.0.4" }, "scripts": { "dev": "nuxt", diff --git a/examples/vue-class-component/pages/about.vue b/examples/vue-class-component/pages/about.vue index 484c798b82..cbe04d7fda 100644 --- a/examples/vue-class-component/pages/about.vue +++ b/examples/vue-class-component/pages/about.vue @@ -1,3 +1,6 @@ diff --git a/examples/vue-class-component/pages/index.vue b/examples/vue-class-component/pages/index.vue index ce87828622..35e0116da2 100644 --- a/examples/vue-class-component/pages/index.vue +++ b/examples/vue-class-component/pages/index.vue @@ -1,41 +1,18 @@ diff --git a/examples/vue-class-component/plugins/nuxt-class-component.js b/examples/vue-class-component/plugins/nuxt-class-component.js deleted file mode 100644 index 2f5188d00b..0000000000 --- a/examples/vue-class-component/plugins/nuxt-class-component.js +++ /dev/null @@ -1,14 +0,0 @@ -import Component from 'vue-class-component' - -Component.registerHooks([ - 'beforeRouteEnter', - 'beforeRouteLeave', - 'asyncData', - 'fetch', - 'middleware', - 'layout', - 'transition', - 'scrollToTop' -]) - -export default Component diff --git a/examples/vuex-persistedstate/README.md b/examples/vuex-persistedstate/README.md new file mode 100644 index 0000000000..6827f6cf6a --- /dev/null +++ b/examples/vuex-persistedstate/README.md @@ -0,0 +1,3 @@ +# Nuxt.js with Vuex persisted state (localStorage) + +See https://github.com/robinvdvleuten/vuex-persistedstate diff --git a/examples/vuex-persistedstate/nuxt.config.js b/examples/vuex-persistedstate/nuxt.config.js new file mode 100644 index 0000000000..274248d91b --- /dev/null +++ b/examples/vuex-persistedstate/nuxt.config.js @@ -0,0 +1,6 @@ +module.exports = { + /* + ** We set `spa` mode to have only client-side rendering + */ + mode: 'spa' +} diff --git a/examples/vuex-persistedstate/package.json b/examples/vuex-persistedstate/package.json new file mode 100644 index 0000000000..5d9eef3ddf --- /dev/null +++ b/examples/vuex-persistedstate/package.json @@ -0,0 +1,12 @@ +{ + "name": "nuxt-vuex-store", + "dependencies": { + "nuxt": "latest", + "vuex-persistedstate": "^2.0.0" + }, + "scripts": { + "dev": "nuxt", + "build": "nuxt build", + "start": "nuxt start" + } +} diff --git a/examples/vuex-persistedstate/pages/index.vue b/examples/vuex-persistedstate/pages/index.vue new file mode 100644 index 0000000000..8540d23c60 --- /dev/null +++ b/examples/vuex-persistedstate/pages/index.vue @@ -0,0 +1,22 @@ + + + diff --git a/examples/vuex-persistedstate/store/index.js b/examples/vuex-persistedstate/store/index.js new file mode 100644 index 0000000000..75a8434719 --- /dev/null +++ b/examples/vuex-persistedstate/store/index.js @@ -0,0 +1,14 @@ +import createPersistedState from 'vuex-persistedstate' + +export const state = () => ({ + counter: 0 +}) + +export const mutations = { + increment: (state) => state.counter++, + decrement: (state) => state.counter-- +} + +export const plugins = [ + createPersistedState() +] diff --git a/examples/vuex-store-modules/pages/index.vue b/examples/vuex-store-modules/pages/index.vue index 99a2cd37a5..33035823fc 100644 --- a/examples/vuex-store-modules/pages/index.vue +++ b/examples/vuex-store-modules/pages/index.vue @@ -23,14 +23,16 @@ import { mapState } from 'vuex' export default { // fetch(context) is called by the server-side // and before instantiating the component - fetch ({ store }) { + fetch({ store }) { store.commit('increment') }, computed: mapState([ 'counter' ]), methods: { - increment () { this.$store.commit('increment') } + increment() { + this.$store.commit('increment') + } } } diff --git a/examples/vuex-store-modules/pages/todos.vue b/examples/vuex-store-modules/pages/todos.vue index a3aef45096..3c21b92a22 100644 --- a/examples/vuex-store-modules/pages/todos.vue +++ b/examples/vuex-store-modules/pages/todos.vue @@ -20,7 +20,7 @@ export default { todos: 'todos/todos' }), methods: { - addTodo (e) { + addTodo(e) { var text = e.target.value if (text.trim()) { this.$store.commit('todos/add', { text }) diff --git a/examples/vuex-store-modules/store/articles.js b/examples/vuex-store-modules/store/articles.js index e2fb76f674..a19b962c88 100644 --- a/examples/vuex-store-modules/store/articles.js +++ b/examples/vuex-store-modules/store/articles.js @@ -7,13 +7,13 @@ export const state = () => ({ }) export const mutations = { - add (state, title) { + add(state, title) { state.list.push(title) } } export const getters = { - get (state) { + get(state) { return state.list } } diff --git a/examples/vuex-store-modules/store/articles/comments.js b/examples/vuex-store-modules/store/articles/comments.js index dd7f3c47c0..4092086eee 100644 --- a/examples/vuex-store-modules/store/articles/comments.js +++ b/examples/vuex-store-modules/store/articles/comments.js @@ -7,13 +7,13 @@ export const state = () => ({ }) export const mutations = { - add (state, title) { + add(state, title) { state.list.push(title) } } export const getters = { - get (state) { + get(state) { return state.list } } diff --git a/examples/vuex-store-modules/store/index.js b/examples/vuex-store-modules/store/index.js index 63f1975bf2..b22221c9c7 100644 --- a/examples/vuex-store-modules/store/index.js +++ b/examples/vuex-store-modules/store/index.js @@ -3,7 +3,7 @@ export const state = () => ({ }) export const mutations = { - increment (state) { + increment(state) { state.counter++ } } diff --git a/examples/vuex-store-modules/store/todos.js b/examples/vuex-store-modules/store/todos.js index 5063cbd1b8..f482ec1242 100644 --- a/examples/vuex-store-modules/store/todos.js +++ b/examples/vuex-store-modules/store/todos.js @@ -3,20 +3,20 @@ export const state = () => ({ }) export const mutations = { - add (state, { text }) { + add(state, { text }) { state.list.push({ text, done: false }) }, - toggle (state, todo) { + toggle(state, todo) { todo.done = !todo.done } } export const getters = { - todos (state) { + todos(state) { return state.list } } diff --git a/examples/vuex-store/pages/index.vue b/examples/vuex-store/pages/index.vue index 3ebbfdcac6..7406a84dd8 100644 --- a/examples/vuex-store/pages/index.vue +++ b/examples/vuex-store/pages/index.vue @@ -13,14 +13,16 @@ import { mapState } from 'vuex' export default { // fetch(context) is called by the server-side // and nuxt before instantiating the component - fetch ({ store }) { + fetch({ store }) { store.commit('increment') }, computed: mapState([ 'counter' ]), methods: { - increment () { this.$store.commit('increment') } + increment() { + this.$store.commit('increment') + } } } diff --git a/examples/vuex-store/store/mutations.js b/examples/vuex-store/store/mutations.js index e85cce517e..81783007a1 100644 --- a/examples/vuex-store/store/mutations.js +++ b/examples/vuex-store/store/mutations.js @@ -1,7 +1,7 @@ const mutations = { - increment (state) { - state.counter++ - } + increment(state) { + state.counter++ + } } -export default mutations \ No newline at end of file +export default mutations diff --git a/examples/with-apollo/README.md b/examples/with-apollo/README.md deleted file mode 100644 index d4600552fb..0000000000 --- a/examples/with-apollo/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# nuxt-with-apollo - -> Nuxt.js with Apollo (GraphQL client) - -[DEMO](https://nuxt-apollo.now.sh/) - -## About - -This project uses [Apollo](http://www.apollodata.com/) as a GraphQL client and [Graphcool](https://www.graph.cool/) as a hosted GraphQL backend. - -## Getting Started - -Download this example [or clone the repo](https://github.com/nuxt/nuxt.js): - -```bash -curl https://codeload.github.com/nuxt/nuxt.js/tar.gz/master | tar -xz --strip=2 nuxt.js-master/examples/with-apollo -cd with-apollo -``` - -Install and run: - -```bash -npm install -npm run dev - -# or with Yarn -yarn -yarn dev -``` diff --git a/examples/with-apollo/layouts/default.vue b/examples/with-apollo/layouts/default.vue deleted file mode 100644 index 62147ffc78..0000000000 --- a/examples/with-apollo/layouts/default.vue +++ /dev/null @@ -1,19 +0,0 @@ - - - diff --git a/examples/with-apollo/layouts/error.vue b/examples/with-apollo/layouts/error.vue deleted file mode 100644 index 192163dd31..0000000000 --- a/examples/with-apollo/layouts/error.vue +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/examples/with-apollo/package.json b/examples/with-apollo/package.json deleted file mode 100644 index c3bb76f0f4..0000000000 --- a/examples/with-apollo/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "nuxt-apollo", - "version": "1.0.0", - "description": "Nuxt.js with Apollo", - "author": "Charlie Hield", - "license": "MIT", - "scripts": { - "dev": "nuxt", - "build": "nuxt build", - "start": "nuxt start" - }, - "keywords": [ - "nuxt", - "vue", - "apollo", - "graphql" - ], - "dependencies": { - "apollo-client": "^1.0.2", - "graphql-tag": "^2.0.0", - "isomorphic-fetch": "^2.2.1", - "nuxt": "^0.10.5" - } -} diff --git a/examples/with-apollo/pages/car/_id.vue b/examples/with-apollo/pages/car/_id.vue deleted file mode 100644 index 955b9475ec..0000000000 --- a/examples/with-apollo/pages/car/_id.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - - - diff --git a/examples/with-apollo/pages/index.vue b/examples/with-apollo/pages/index.vue deleted file mode 100644 index b8c6a7d7df..0000000000 --- a/examples/with-apollo/pages/index.vue +++ /dev/null @@ -1,59 +0,0 @@ - - - - - diff --git a/examples/with-apollo/plugins/apollo.js b/examples/with-apollo/plugins/apollo.js deleted file mode 100644 index f328ff1fc7..0000000000 --- a/examples/with-apollo/plugins/apollo.js +++ /dev/null @@ -1,15 +0,0 @@ -import Vue from 'vue' -import { ApolloClient, createNetworkInterface } from 'apollo-client' -import 'isomorphic-fetch' - -// Created with Graphcool - https://www.graph.cool/ -const API_ENDPOINT = 'https://api.graph.cool/simple/v1/cj1dqiyvqqnmj0113yuqamkuu' - -const apolloClient = new ApolloClient({ - networkInterface: createNetworkInterface({ - uri: API_ENDPOINT, - transportBatching: true - }) -}) - -export default apolloClient diff --git a/examples/with-ava/package.json b/examples/with-ava/package.json index f8925f9997..02065f4c57 100755 --- a/examples/with-ava/package.json +++ b/examples/with-ava/package.json @@ -11,6 +11,6 @@ "jsdom": "^11.0.0" }, "dependencies": { - "nuxt": "^1.0.0-alpha2" + "nuxt": "latest" } } diff --git a/examples/with-ava/pages/index.vue b/examples/with-ava/pages/index.vue index a3d80ae170..b411fa4762 100755 --- a/examples/with-ava/pages/index.vue +++ b/examples/with-ava/pages/index.vue @@ -4,7 +4,7 @@ + + diff --git a/examples/with-cookies/plugins/cookies.js b/examples/with-cookies/plugins/cookies.js new file mode 100644 index 0000000000..145baaec12 --- /dev/null +++ b/examples/with-cookies/plugins/cookies.js @@ -0,0 +1,34 @@ +import Vue from 'vue' +import Cookie from 'cookie' +import JSCookie from 'js-cookie' + +// Called only on client-side +export const getCookies = (str) => { + return Cookie.parse(str || '') +} + +/* +** Executed by ~/.nuxt/index.js with context given +** This method can be asynchronous +*/ +export default ({ req }, inject) => { + // Inject `cookies` key + // -> app.$cookies + // -> this.$cookies in vue components + // -> this.$cookies in store actions/mutations + inject('cookies', new Vue({ + data: () => ({ + cookies: getCookies(process.server ? req.headers.cookie : document.cookie) + }), + methods: { + set(...args) { + JSCookie.set(...args) + this.cookies = getCookies(document.cookie) + }, + remove(...args) { + JSCookie.remove(...args) + this.cookies = getCookies(document.cookie) + } + } + })) +} diff --git a/examples/with-element-ui/layouts/default.vue b/examples/with-element-ui/layouts/default.vue new file mode 100644 index 0000000000..184d6f6fd3 --- /dev/null +++ b/examples/with-element-ui/layouts/default.vue @@ -0,0 +1,10 @@ + diff --git a/examples/with-element-ui/nuxt.config.js b/examples/with-element-ui/nuxt.config.js new file mode 100644 index 0000000000..643f79ee78 --- /dev/null +++ b/examples/with-element-ui/nuxt.config.js @@ -0,0 +1,15 @@ +module.exports = { + /* + ** Global CSS + */ + css: [ + 'element-ui/lib/theme-default/index.css' + ], + + /* + ** Add element-ui in our app, see plugins/element-ui.js file + */ + plugins: [ + '@/plugins/element-ui' + ] +} diff --git a/examples/with-element-ui/package.json b/examples/with-element-ui/package.json new file mode 100644 index 0000000000..d5696a30ea --- /dev/null +++ b/examples/with-element-ui/package.json @@ -0,0 +1,18 @@ +{ + "name": "with-element-ui", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "element-ui": "^1.4.9", + "nuxt": "latest" + }, + "scripts": { + "dev": "nuxt", + "build": "nuxt build", + "start": "nuxt start" + }, + "devDependencies": { + "node-sass": "^4.6.0", + "sass-loader": "^6.0.6" + } +} diff --git a/examples/with-element-ui/pages/index.vue b/examples/with-element-ui/pages/index.vue new file mode 100644 index 0000000000..69c4a92771 --- /dev/null +++ b/examples/with-element-ui/pages/index.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/examples/with-element-ui/plugins/element-ui.js b/examples/with-element-ui/plugins/element-ui.js new file mode 100644 index 0000000000..cb618add96 --- /dev/null +++ b/examples/with-element-ui/plugins/element-ui.js @@ -0,0 +1,7 @@ +import Vue from 'vue' +import Element from 'element-ui/lib/element-ui.common' +import locale from 'element-ui/lib/locale/lang/en' + +export default () => { + Vue.use(Element, { locale }) +} diff --git a/examples/with-feathers/.eslintrc.js b/examples/with-feathers/.eslintrc.js new file mode 100644 index 0000000000..0bdc518e5c --- /dev/null +++ b/examples/with-feathers/.eslintrc.js @@ -0,0 +1,36 @@ +module.exports = { + root: true, + parser: 'babel-eslint', + parserOptions: { + sourceType: 'module' + }, + env: { + browser: true, + node: true, + mocha: true + }, + extends: 'standard', + // required to lint *.vue files + plugins: [ + 'html' + ], + // add your custom rules here + rules: { + // allow paren-less arrow functions + 'arrow-parens': 0, + // allow async-await + 'generator-star-spacing': 0, + // allow debugger during development + 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, + // do not allow console.logs etc... + 'no-console': 2, + 'space-before-function-paren': [ + 2, + { + anonymous: 'always', + named: 'never' + } + ], + }, + globals: {} +} diff --git a/examples/with-feathers/nuxt.config.js b/examples/with-feathers/nuxt.config.js index f982017753..1fec96a57b 100644 --- a/examples/with-feathers/nuxt.config.js +++ b/examples/with-feathers/nuxt.config.js @@ -2,4 +2,4 @@ module.exports = { loading: { color: 'purple' } -}; +} diff --git a/examples/with-feathers/package.json b/examples/with-feathers/package.json index 3468c1aead..fc99464a7f 100644 --- a/examples/with-feathers/package.json +++ b/examples/with-feathers/package.json @@ -37,6 +37,7 @@ "winston": "^2.3.0" }, "devDependencies": { + "eslint-plugin-mocha": "^4.11.0", "jshint": "^2.9.4", "mocha": "^3.2.0", "nodemon": "^1.11.0", diff --git a/examples/with-feathers/pages/about.vue b/examples/with-feathers/pages/about.vue index 2dcf808b30..a16b409edc 100644 --- a/examples/with-feathers/pages/about.vue +++ b/examples/with-feathers/pages/about.vue @@ -7,9 +7,9 @@ diff --git a/examples/with-firebase/pages/users/_key.vue b/examples/with-firebase/pages/users/_key.vue index 94a2735870..410842a241 100644 --- a/examples/with-firebase/pages/users/_key.vue +++ b/examples/with-firebase/pages/users/_key.vue @@ -11,15 +11,13 @@ diff --git a/examples/with-museui/nuxt.config.js b/examples/with-museui/nuxt.config.js new file mode 100644 index 0000000000..55a86f08c1 --- /dev/null +++ b/examples/with-museui/nuxt.config.js @@ -0,0 +1,26 @@ +module.exports = { + head: { + meta: [ + { + name: 'viewport', + content: 'width=device-width, initial-scale=1' + } + ], + link: [ + { + rel: 'stylesheet', + href: + 'https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic' + }, + { + rel: 'stylesheet', + href: 'https://fonts.googleapis.com/icon?family=Material+Icons' + }, + { + rel: 'stylesheet', + href: 'https://unpkg.com/muse-ui@2.1.0/dist/muse-ui.css' + } + ] + }, + plugins: ['~/plugins/museui'] +} diff --git a/examples/with-museui/package.json b/examples/with-museui/package.json new file mode 100644 index 0000000000..6f88ac897c --- /dev/null +++ b/examples/with-museui/package.json @@ -0,0 +1,12 @@ +{ + "name": "with-museui", + "dependencies": { + "nuxt": "latest", + "muse-ui": "latest" + }, + "scripts": { + "dev": "nuxt", + "build": "nuxt build", + "start": "nuxt start" + } +} diff --git a/examples/with-museui/pages/index.vue b/examples/with-museui/pages/index.vue new file mode 100644 index 0000000000..0d1973e82f --- /dev/null +++ b/examples/with-museui/pages/index.vue @@ -0,0 +1,43 @@ + + + + + diff --git a/examples/with-museui/plugins/museui.js b/examples/with-museui/plugins/museui.js new file mode 100644 index 0000000000..623286c123 --- /dev/null +++ b/examples/with-museui/plugins/museui.js @@ -0,0 +1,4 @@ +import Vue from 'vue' +import MuseUI from 'muse-ui' + +Vue.use(MuseUI) diff --git a/examples/with-sockets/io/index.js b/examples/with-sockets/io/index.js new file mode 100644 index 0000000000..53560a8eb7 --- /dev/null +++ b/examples/with-sockets/io/index.js @@ -0,0 +1,24 @@ +module.exports = function () { + const server = require('http').createServer(this.nuxt.renderer.app) + const io = require('socket.io')(server) + + // overwrite nuxt.listen() + this.nuxt.listen = (port, host) => new Promise((resolve) => server.listen(port || 3000, host || 'localhost', resolve)) + // close this server on 'close' event + this.nuxt.plugin('close', () => new Promise((resolve) => server.close(resolve))) + + // Add `socket.io-client` in vendor + this.addVendor('socket.io-client') + + // Add socket.io events + let messages = [] + io.on('connection', (socket) => { + socket.on('last-messages', function (fn) { + fn(messages.slice(-50)) + }) + socket.on('send-message', function (message) { + messages.push(message) + socket.broadcast.emit('new-message', message) + }) + }) +} diff --git a/examples/with-sockets/nuxt.config.js b/examples/with-sockets/nuxt.config.js index 7ca9c60b1f..34e2ec30a1 100644 --- a/examples/with-sockets/nuxt.config.js +++ b/examples/with-sockets/nuxt.config.js @@ -1,14 +1,12 @@ module.exports = { - build: { - vendor: ['socket.io-client'] - }, head: { meta: [ { charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' } ] }, + modules: ['~/io'], env: { - HOST_URL: process.env.HOST_URL || 'http://localhost:3000' + WS_URL: process.env.WS_URL || 'http://localhost:3000' } } diff --git a/examples/with-sockets/package.json b/examples/with-sockets/package.json index 4f4c523957..b29200ff34 100644 --- a/examples/with-sockets/package.json +++ b/examples/with-sockets/package.json @@ -7,7 +7,7 @@ }, "dependencies": { "express": "^4.14.0", - "nuxt": "^0.9.5", + "nuxt": "latest", "socket.io": "^1.7.2", "socket.io-client": "^1.7.2" }, diff --git a/examples/with-sockets/pages/index.vue b/examples/with-sockets/pages/index.vue index 4e4f66c714..7184116d06 100644 --- a/examples/with-sockets/pages/index.vue +++ b/examples/with-sockets/pages/index.vue @@ -14,10 +14,10 @@ + + diff --git a/examples/with-tape/test/index.test.js b/examples/with-tape/test/index.test.js new file mode 100755 index 0000000000..7d46c3d11e --- /dev/null +++ b/examples/with-tape/test/index.test.js @@ -0,0 +1,43 @@ +import test from 'tape' +import { shallow } from 'vue-test-utils' +import Index from '../pages/index.vue' + +test('renders Index.vue correctly', t => { + t.plan(4) + + const wrapper = shallow(Index, { + data: { + name: 'nuxt' + } + }) + + const button = wrapper.find('button') + + t.equal( + wrapper.find('h1').text(), + 'Hello nuxt!', + 'renders "Hello nuxt!" text' + ) + + t.equal( + wrapper.find('h1').hasClass('red'), + true, + 'h1 has a red class [default]' + ) + + button.trigger('click') + + t.equal( + wrapper.find('h1').hasClass('blue'), + true, + 'h1 class changes to blue [after 1st click]' + ) + + button.trigger('click') + + t.equal( + wrapper.find('h1').hasClass('green'), + true, + 'h1 class changes to green [after 2nd click]' + ) +}) diff --git a/examples/with-tape/test/setup.js b/examples/with-tape/test/setup.js new file mode 100644 index 0000000000..69cf3aa353 --- /dev/null +++ b/examples/with-tape/test/setup.js @@ -0,0 +1,14 @@ +const hooks = require('require-extension-hooks') + +// Setup browser environment +require('browser-env')() + +// Setup vue files to be processed by `require-extension-hooks-vue` +hooks('vue') + .plugin('vue') + .push() + +// Setup vue and js files to be processed by `require-extension-hooks-babel` +hooks(['vue', 'js']) + .plugin('babel') + .push() diff --git a/examples/with-vuetify/css/vendor/vuetify.styl b/examples/with-vuetify/assets/app.styl similarity index 86% rename from examples/with-vuetify/css/vendor/vuetify.styl rename to examples/with-vuetify/assets/app.styl index 6865590deb..66d746eb17 100644 --- a/examples/with-vuetify/css/vendor/vuetify.styl +++ b/examples/with-vuetify/assets/app.styl @@ -11,4 +11,4 @@ $theme := { } // Import Vuetify styling -@require '~vuetify/src/stylus/main.styl' +@require '~vuetify/src/stylus/main.styl' \ No newline at end of file diff --git a/examples/with-vuetify/css/app.styl b/examples/with-vuetify/css/app.styl deleted file mode 100644 index cd1c8f9dc8..0000000000 --- a/examples/with-vuetify/css/app.styl +++ /dev/null @@ -1,2 +0,0 @@ -@require './vendor/material-icons.styl' -@require './vendor/vuetify.styl' diff --git a/examples/with-vuetify/css/vendor/material-icons.styl b/examples/with-vuetify/css/vendor/material-icons.styl deleted file mode 100644 index a063664368..0000000000 --- a/examples/with-vuetify/css/vendor/material-icons.styl +++ /dev/null @@ -1,21 +0,0 @@ -@font-face { - font-family: 'Material Icons'; - font-style: normal; - font-weight: 400; - src: local('Material Icons'), local('MaterialIcons-Regular'), url(https://fonts.gstatic.com/s/materialicons/v22/2fcrYFNaTjcS6g4U3t-Y5UEw0lE80llgEseQY3FEmqw.woff2) format('woff2'); -} -.material-icons { - font-family: 'Material Icons'; - font-weight: normal; - font-style: normal; - font-size: 24px; - line-height: 1; - letter-spacing: normal; - text-transform: none; - display: inline-block; - white-space: nowrap; - word-wrap: normal; - direction: ltr; - -webkit-font-feature-settings: 'liga'; - -webkit-font-smoothing: antialiased; -} diff --git a/examples/with-vuetify/layouts/default.vue b/examples/with-vuetify/layouts/default.vue new file mode 100644 index 0000000000..7c8e435f8a --- /dev/null +++ b/examples/with-vuetify/layouts/default.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/examples/with-vuetify/nuxt.config.js b/examples/with-vuetify/nuxt.config.js index a01ebf437d..df74955493 100644 --- a/examples/with-vuetify/nuxt.config.js +++ b/examples/with-vuetify/nuxt.config.js @@ -1,17 +1,26 @@ - -const { join } = require('path') - module.exports = { - build: { - vendor: ['vuetify'] - }, - plugins: ['~plugins/vuetify.js'], - css: [ - { src: join(__dirname, 'css/app.styl'), lang: 'styl' } - ], + /* + ** Head elements + ** Add Roboto font and Material Icons + */ head: { link: [ - { rel: 'preload', as: 'style', href: 'https://fonts.googleapis.com/css?family=Roboto' } + { rel: 'stylesheet', type: 'text/css', href: 'https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' } ] - } + }, + /* + ** Add Vuetify into vendor.bundle.js + */ + build: { + vendor: ['vuetify'], + extractCSS: true + }, + /* + ** Load Vuetify into the app + */ + plugins: ['~/plugins/vuetify'], + /* + ** Load Vuetify CSS globally + */ + css: ['~/assets/app.styl'] } diff --git a/examples/with-vuetify/package.json b/examples/with-vuetify/package.json index baa308ca69..646947ae35 100644 --- a/examples/with-vuetify/package.json +++ b/examples/with-vuetify/package.json @@ -1,16 +1,14 @@ { "name": "with-vuetify", "dependencies": { - "nuxt": "^0.10.7", - "vuetify": "^0.11.1" + "nuxt": "latest", + "vuetify": "latest" }, "scripts": { "dev": "nuxt", "build": "nuxt build", "start": "nuxt start", - "generate": "nuxt generate", - "predeploy": "yarn run generate", - "deploy": "surge --domain nuxt-with-vuetify-example.surge.sh dist" + "generate": "nuxt generate" }, "devDependencies": { "stylus": "^0.54.5", diff --git a/examples/with-vuetify/pages/about.vue b/examples/with-vuetify/pages/about.vue new file mode 100644 index 0000000000..4a926dad1b --- /dev/null +++ b/examples/with-vuetify/pages/about.vue @@ -0,0 +1,18 @@ + + + \ No newline at end of file diff --git a/examples/with-vuetify/pages/index.vue b/examples/with-vuetify/pages/index.vue index 80916b3b54..52573bcb7d 100644 --- a/examples/with-vuetify/pages/index.vue +++ b/examples/with-vuetify/pages/index.vue @@ -1,45 +1,8 @@ - - - - +
    +

    Main content

    + Primary button + Secondary button + Success button +
    + \ No newline at end of file diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index 29b48d25f3..0000000000 --- a/index.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -//These declarations allow TypeScript to import non-js/ts files without the file extensions (such as .vue files) - -declare module "~components/*" {} -declare module "~layouts/*" {} -declare module "~pages/*" {} -declare module "~assets/*" {} -declare module "~static/*" {} diff --git a/index.js b/index.js index 8e7487a896..24e035805e 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,16 @@ /*! * Nuxt.js * (c) 2016-2017 Chopin Brothers + * Core maintainer: Pooya (@pi0) * Released under the MIT License. */ +// Node Source Map Support +// https://github.com/evanw/node-source-map-support +require('source-map-support').install() + +// Fix babel flag +/* istanbul ignore else */ process.noDeprecation = true -var Nuxt = require('./dist/nuxt.js') - -module.exports = Nuxt.default ? Nuxt.default : Nuxt +module.exports = require('./dist/nuxt') diff --git a/lib/app/App.js b/lib/app/App.js new file mode 100644 index 0000000000..0d0e719837 --- /dev/null +++ b/lib/app/App.js @@ -0,0 +1,109 @@ +import Vue from 'vue' +<% if (loading) { %>import NuxtLoading from '<%= (typeof loading === "string" ? loading : "./components/nuxt-loading.vue") %>'<% } %> +<% css.forEach(function (c) { %> +import '<%= relativeToBuild(resolvePath(c.src || c)) %>' +<% }) %> + +let layouts = { +<% +var layoutsKeys = Object.keys(layouts); +layoutsKeys.forEach(function (key, i) { %> + "_<%= key %>": () => import('<%= layouts[key] %>' /* webpackChunkName: "<%= wChunk('layouts/'+key) %>" */).then(m => m.default || m)<%= (i + 1) < layoutsKeys.length ? ',' : '' %> +<% }) %> +} + +let resolvedLayouts = {} + +export default { + head: <%= serialize(head).replace('head(', 'function(').replace('titleTemplate(', 'function(') %>, + render(h, props) { + <% if (loading) { %>const loadingEl = h('nuxt-loading', { ref: 'loading' })<% } %> + const layoutEl = h(this.layout || 'nuxt') + const templateEl = h('div', { + domProps: { + id: '__layout' + }, + key: this.layoutName + }, [ layoutEl ]) + + const transitionEl = h('transition', { + props: { + name: '<%= layoutTransition.name %>', + mode: '<%= layoutTransition.mode %>' + } + }, [ templateEl ]) + + return h('div',{ + domProps: { + id: '__nuxt' + } + }, [ + <% if (loading) { %>loadingEl,<% } %> + transitionEl + ]) + }, + data: () => ({ + layout: null, + layoutName: '' + }), + beforeCreate () { + Vue.util.defineReactive(this, 'nuxt', this.$options.nuxt) + }, + created () { + // Add this.$nuxt in child instances + Vue.prototype.$nuxt = this + // add to window so we can listen when ready + if (typeof window !== 'undefined') { + window.$nuxt = this + } + // Add $nuxt.error() + this.error = this.nuxt.error + }, + <% if (loading) { %> + mounted () { + this.$loading = this.$refs.loading + }, + watch: { + 'nuxt.err': 'errorChanged' + }, + <% } %> + methods: { + <% if (loading) { %> + errorChanged () { + if (this.nuxt.err && this.$loading) { + if (this.$loading.fail) this.$loading.fail() + if (this.$loading.finish) this.$loading.finish() + } + }, + <% } %> + setLayout (layout) { + if (!layout || !resolvedLayouts['_' + layout]) layout = 'default' + this.layoutName = layout + let _layout = '_' + layout + this.layout = resolvedLayouts[_layout] + return this.layout + }, + loadLayout (layout) { + if (!layout || !(layouts['_' + layout] || resolvedLayouts['_' + layout])) layout = 'default' + let _layout = '_' + layout + if (resolvedLayouts[_layout]) { + return Promise.resolve(resolvedLayouts[_layout]) + } + return layouts[_layout]() + .then((Component) => { + resolvedLayouts[_layout] = Component + delete layouts[_layout] + return resolvedLayouts[_layout] + }) + .catch((e) => { + if (this.$nuxt) { + return this.$nuxt.error({ statusCode: 500, message: e.message }) + } + }) + } + }, + components: { + <%= (loading ? 'NuxtLoading' : '') %> + } +} + diff --git a/lib/app/App.vue b/lib/app/App.vue deleted file mode 100644 index fa8ef6d403..0000000000 --- a/lib/app/App.vue +++ /dev/null @@ -1,66 +0,0 @@ - - - - diff --git a/lib/app/client.js b/lib/app/client.js index ccb2381d02..216b8ded79 100644 --- a/lib/app/client.js +++ b/lib/app/client.js @@ -1,185 +1,354 @@ -'use strict' - import Vue from 'vue' import middleware from './middleware' import { createApp, NuxtError } from './index' -import { applyAsyncData, sanitizeComponent, getMatchedComponents, getMatchedComponentsInstances, flatMapComponents, getContext, middlewareSeries, promisify, getLocation, compile } from './utils' +import { + applyAsyncData, + sanitizeComponent, + resolveRouteComponents, + getMatchedComponents, + getMatchedComponentsInstances, + flatMapComponents, + setContext, + middlewareSeries, + promisify, + getLocation, + compile, + getQueryDiff +} from './utils' + const noopData = () => { return {} } const noopFetch = () => {} -let _lastPaths = [] -let _lastComponentsFiles = [] +// Global shared references +let _lastPaths = [] let app let router -<%= (store ? 'let store' : '') %> +<% if (store) { %>let store<% } %> + +// Try to rehydrate SSR data from window +const NUXT = window.__NUXT__ || {} + +<% if (debug || mode === 'spa') { %> +// Setup global Vue error handler +const defaultErrorHandler = Vue.config.errorHandler +Vue.config.errorHandler = function (err, vm, info) { + const nuxtError = { + statusCode: err.statusCode || err.name || 'Whoops!', + message: err.message || err.toString() + } + + // Show Nuxt Error Page + if(vm && vm.$root && vm.$root.$nuxt && vm.$root.$nuxt.error && info !== 'render function') { + vm.$root.$nuxt.error(nuxtError) + } + + // Call other handler if exist + if (typeof defaultErrorHandler === 'function') { + return defaultErrorHandler(...arguments) + } + + // Log to console + if (process.env.NODE_ENV !== 'production') { + console.error(err) + } else { + console.error(err.message || nuxtError.message) + } +} +<% } %> + +// Create and mount App +createApp() +.then(mountApp) +.catch(err => { + console.error('[nuxt] Error while initializing app', err) +}) + +function componentOption(component, key, ...args) { + if (!component || !component.options || !component.options[key]) { + return {} + } + const option = component.options[key] + if (typeof option === 'function') { + return option(...args) + } + return option +} function mapTransitions(Components, to, from) { - return Components.map((Component) => { - let transition = Component.options.transition - if (typeof transition === 'function') { - return transition(to, from) + const componentTransitions = component => { + const transition = componentOption(component, 'transition', to, from) || {} + return (typeof transition === 'string' ? { name: transition } : transition) + } + + return Components.map(Component => { + // Clone original object to prevent overrides + const transitions = Object.assign({}, componentTransitions(Component)) + + // Combine transitions & prefer `leave` transitions of 'from' route + if (from && from.matched.length && from.matched[0].components.default) { + const from_transitions = componentTransitions(from.matched[0].components.default) + Object.keys(from_transitions) + .filter(key => from_transitions[key] && key.toLowerCase().indexOf('leave') !== -1) + .forEach(key => { transitions[key] = from_transitions[key] }) } - return transition + + return transitions }) } -function loadAsyncComponents (to, from, next) { - const resolveComponents = flatMapComponents(to, (Component, _, match, key) => { - if (typeof Component === 'function' && !Component.options) { - return new Promise(function (resolve, reject) { - const _resolve = (Component) => { - Component = sanitizeComponent(Component) - match.components[key] = Component - resolve(Component) - } - Component().then(_resolve).catch(reject) - }) - } - Component = sanitizeComponent(Component) - match.components[key] = Component - return match.components[key] - }) - const fromPath = from.fullPath.split('#')[0] - const toPath = to.fullPath.split('#')[0] - this._hashChanged = (fromPath === toPath) - if (!this._hashChanged) { - <%= (loading ? 'this.$loading.start && this.$loading.start()' : '') %> +async function loadAsyncComponents (to, from, next) { + // Check if route path changed (this._pathChanged), only if the page is not an error (for validate()) + this._pathChanged = !!app.nuxt.err || from.path !== to.path + this._queryChanged = JSON.stringify(to.query) !== JSON.stringify(from.query) + this._diffQuery = (this._queryChanged ? getQueryDiff(to.query, from.query) : []) + + <% if (loading) { %> + if (this._pathChanged && this.$loading.start) { + this.$loading.start() } - Promise.all(resolveComponents) - .then(() => next()) - .catch((err) => { - let statusCode = err.statusCode || err.status || (err.response && err.response.status) || 500 + <% } %> + + try { + const Components = await resolveRouteComponents(to) + <% if (loading) { %> + if (!this._pathChanged && this._queryChanged) { + // Add a marker on each component that it needs to refresh or not + const startLoader = Components.some((Component) => { + const watchQuery = Component.options.watchQuery + if (watchQuery === true) return true + if (Array.isArray(watchQuery)) { + return watchQuery.some((key) => this._diffQuery[key]) + } + return false + }) + if (startLoader && this.$loading.start) { + this.$loading.start() + } + } + <% } %> + // Call next() + next() + } catch (err) { + err = err || {} + const statusCode = err.statusCode || err.status || (err.response && err.response.status) || 500 this.error({ statusCode, message: err.message }) + this.$nuxt.$emit('routeChanged', to, from, err) next(false) + } +} + +function applySSRData(Component, ssrData) { + if (NUXT.serverRendered && ssrData) { + applyAsyncData(Component, ssrData) + } + Component._Ctor = Component + return Component +} + +// Get matched components +function resolveComponents(router) { + const path = getLocation(router.options.base, router.options.mode) + + return flatMapComponents(router.match(path), async (Component, _, match, key, index) => { + // If component is not resolved yet, resolve it + if (typeof Component === 'function' && !Component.options) { + Component = await Component() + } + // Sanitize it and save it + const _Component = applySSRData(sanitizeComponent(Component), NUXT.data ? NUXT.data[index] : null) + match.components[key] = _Component + return _Component }) } function callMiddleware (Components, context, layout) { - // if layout is undefined, only call global middleware let midd = <%= serialize(router.middleware, { isJSON: true }) %> let unknownMiddleware = false + + // If layout is undefined, only call global middleware if (typeof layout !== 'undefined') { - midd = [] // exclude global middleware if layout defined (already called before) + midd = [] // Exclude global middleware if layout defined (already called before) if (layout.middleware) { midd = midd.concat(layout.middleware) } - Components.forEach((Component) => { + Components.forEach(Component => { if (Component.options.middleware) { midd = midd.concat(Component.options.middleware) } }) } - midd = midd.map((name) => { + + midd = midd.map(name => { + if (typeof name === 'function') return name if (typeof middleware[name] !== 'function') { unknownMiddleware = true this.error({ statusCode: 500, message: 'Unknown middleware ' + name }) } return middleware[name] }) + if (unknownMiddleware) return return middlewareSeries(midd, context) } async function render (to, from, next) { - if (this._hashChanged) return next() - let layout + if (this._pathChanged === false && this._queryChanged === false) return next() + + // nextCalled is true when redirected let nextCalled = false - const _next = function (path) { - <%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %> + const _next = path => { + <% if(loading) { %>if(this.$loading.finish) this.$loading.finish()<% } %> if (nextCalled) return nextCalled = true next(path) } - let context = getContext({ to<%= (store ? ', store' : '') %>, isClient: true, next: _next.bind(this), error: this.error.bind(this) }, app) - let Components = getMatchedComponents(to) - this._context = context - this._dateLastError = this.$options._nuxt.dateErr - this._hadError = !!this.$options._nuxt.err + + // Update context + await setContext(app, { + route: to, + from, + next: _next.bind(this) + }) + this._dateLastError = app.nuxt.dateErr + this._hadError = !!app.nuxt.err + + // Get route's matched components + const Components = getMatchedComponents(to) + + // If no Components matched, generate 404 if (!Components.length) { // Default layout - await callMiddleware.call(this, Components, context) - if (context._redirected) return - layout = await this.loadLayout(typeof NuxtError.layout === 'function' ? NuxtError.layout(context) : NuxtError.layout) - await callMiddleware.call(this, Components, context, layout) - if (context._redirected) return - this.error({ statusCode: 404, message: 'This page could not be found.' }) + await callMiddleware.call(this, Components, app.context) + if (app.context._redirected) return + // Load layout for error page + const layout = await this.loadLayout(typeof NuxtError.layout === 'function' ? NuxtError.layout(app.context) : NuxtError.layout) + await callMiddleware.call(this, Components, app.context, layout) + if (app.context._redirected) return + // Show error page + app.context.error({ statusCode: 404, message: '<%= messages.error_404 %>' }) return next() } + // Update ._data and other properties if hot reloaded - Components.forEach(function (Component) { + Components.forEach(Component => { if (Component._Ctor && Component._Ctor.options) { Component.options.asyncData = Component._Ctor.options.asyncData Component.options.fetch = Component._Ctor.options.fetch } }) + + // Apply transitions this.setTransitions(mapTransitions(Components, to, from)) + try { + // Call middleware + await callMiddleware.call(this, Components, app.context) + if (app.context._redirected) return + if (app.context._errored) return next() + // Set layout - await callMiddleware.call(this, Components, context) - if (context._redirected) return - layout = Components[0].options.layout + let layout = Components[0].options.layout if (typeof layout === 'function') { - layout = layout(context) + layout = layout(app.context) } layout = await this.loadLayout(layout) - await callMiddleware.call(this, Components, context, layout) - if (context._redirected) return - // Pass validation? + + // Call middleware for layout + await callMiddleware.call(this, Components, app.context, layout) + if (app.context._redirected) return + if (app.context._errored) return next() + + // Call .validate() let isValid = true - Components.forEach((Component) => { + Components.forEach(Component => { if (!isValid) return if (typeof Component.options.validate !== 'function') return isValid = Component.options.validate({ params: to.params || {}, - query : to.query || {}<%= (store ? ', store: context.store' : '') %> + query : to.query || {}, + <% if(store) { %>store<% } %> }) }) + // ...If .validate() returned false if (!isValid) { - this.error({ statusCode: 404, message: 'This page could not be found.' }) + this.error({ statusCode: 404, message: '<%= messages.error_404 %>' }) return next() } + + // Call asyncData & fetch hooks on components matched by the route. await Promise.all(Components.map((Component, i) => { // Check if only children route changed Component._path = compile(to.matched[i].path)(to.params) - if (!this._hadError && Component._path === _lastPaths[i] && (i + 1) !== Components.length) { + Component._dataRefresh = false + // Check if Component need to be refreshed (call asyncData & fetch) + // Only if its slug has changed or is watch query changes + if (this._pathChanged && Component._path !== _lastPaths[i]) { + Component._dataRefresh = true + } else if (!this._pathChanged && this._queryChanged) { + const watchQuery = Component.options.watchQuery + if (watchQuery === true) { + Component._dataRefresh = true + } else if (Array.isArray(watchQuery)) { + Component._dataRefresh = watchQuery.some((key) => this._diffQuery[key]) + } + } + if (!this._hadError && this._isMounted && !Component._dataRefresh) { return Promise.resolve() } + let promises = [] - // asyncData method - if (Component.options.asyncData && typeof Component.options.asyncData === 'function') { - var promise = promisify(Component.options.asyncData, context) - promise.then((asyncDataResult) => { + + const hasAsyncData = Component.options.asyncData && typeof Component.options.asyncData === 'function' + const hasFetch = !!Component.options.fetch + <% if(loading) { %>const loadingIncrease = (hasAsyncData && hasFetch) ? 30 : 45<% } %> + + // Call asyncData(context) + if (hasAsyncData) { + const promise = promisify(Component.options.asyncData, app.context) + .then(asyncDataResult => { applyAsyncData(Component, asyncDataResult) - <%= (loading ? 'this.$loading.increase && this.$loading.increase(30)' : '') %> + <% if(loading) { %>if(this.$loading.increase) this.$loading.increase(loadingIncrease)<% } %> }) promises.push(promise) } - if (Component.options.fetch) { - var p = Component.options.fetch(context) - if (!p || (!(p instanceof Promise) && (typeof p.then !== 'function'))) { p = Promise.resolve(p) } - <%= (loading ? 'p.then(() => this.$loading.increase && this.$loading.increase(30))' : '') %> + + // Call fetch(context) + if (hasFetch) { + let p = Component.options.fetch(app.context) + if (!p || (!(p instanceof Promise) && (typeof p.then !== 'function'))) { + p = Promise.resolve(p) + } + p.then(fetchResult => { + <% if(loading) { %>if(this.$loading.increase) this.$loading.increase(loadingIncrease)<% } %> + }) promises.push(p) } + return Promise.all(promises) })) + _lastPaths = Components.map((Component, i) => compile(to.matched[i].path)(to.params)) - <%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %> + + <% if(loading) { %>if(this.$loading.finish) this.$loading.finish()<% } %> + // If not redirected - if (!nextCalled) { - next() - } + if (!nextCalled) next() + } catch (error) { + if (!error) error = {} _lastPaths = [] error.statusCode = error.statusCode || error.status || (error.response && error.response.status) || 500 + + // Load error layout let layout = NuxtError.layout if (typeof layout === 'function') { - layout = layout(context) + layout = layout(app.context) } - this.loadLayout(layout) - .then(() => { - this.error(error) - next(false) - }) + await this.loadLayout(layout) + + this.error(error) + this.$nuxt.$emit('routeChanged', to, from, error) + next(false) } } @@ -196,66 +365,92 @@ function normalizeComponents (to, ___) { }) } +function showNextPage(to) { + // Hide error component if no error + if (this._hadError && this._dateLastError === this.$options.nuxt.dateErr) { + this.error() + } + + // Set layout + let layout = this.$options.nuxt.err ? NuxtError.layout : to.matched[0].components.default.options.layout + if (typeof layout === 'function') { + layout = layout(app.context) + } + this.setLayout(layout) +} + // When navigating on a different route but the same component is used, Vue.js -// will not update the instance data, so we have to update $data ourselves -function fixPrepatch (to, ___) { - if (this._hashChanged) return +// Will not update the instance data, so we have to update $data ourselves +function fixPrepatch(to, ___) { + if (this._pathChanged === false && this._queryChanged === false) return + Vue.nextTick(() => { - let instances = getMatchedComponentsInstances(to) - _lastComponentsFiles = instances.map((instance, i) => { - if (!instance) return ''; - if (_lastPaths[i] === instance.constructor._path && typeof instance.constructor.options.data === 'function') { - let newData = instance.constructor.options.data.call(instance) + const instances = getMatchedComponentsInstances(to) + + instances.forEach((instance, i) => { + if (!instance) return + if (instance.constructor._dataRefresh && _lastPaths[i] === instance.constructor._path && typeof instance.constructor.options.data === 'function') { + const newData = instance.constructor.options.data.call(instance) for (let key in newData) { Vue.set(instance.$data, key, newData[key]) } } - return instance.constructor.options.__file }) - // hide error component if no error - if (this._hadError && this._dateLastError === this.$options._nuxt.dateErr) { - this.error() - } - // Set layout - let layout = this.$options._nuxt.err ? NuxtError.layout : to.matched[0].components.default.options.layout - if (typeof layout === 'function') { - layout = layout(this._context) - } - this.setLayout(layout) - // hot reloading + showNextPage.call(this, to) + <% if (isDev) { %> + // Hot reloading setTimeout(() => hotReloadAPI(this), 100) + <% } %> }) } +function nuxtReady (_app) { + window._nuxtReadyCbs.forEach((cb) => { + if (typeof cb === 'function') { + cb(_app) + } + }) + // Special JSDOM + if (typeof window._onNuxtLoaded === 'function') { + window._onNuxtLoaded(_app) + } + // Add router hooks + router.afterEach(function (to, from) { + // Wait for fixPrepatch + $data updates + Vue.nextTick(() => _app.$nuxt.$emit('routeChanged', to, from)) + }) +} + +<% if (isDev) { %> // Special hot reload with asyncData(context) +function getNuxtChildComponents($parent, $components = []) { + $parent.$children.forEach(($child) => { + if ($child.$vnode.data.nuxtChild && !$components.find(c =>(c.$options.__file === $child.$options.__file))) { + $components.push($child) + } + if ($child.$children && $child.$children.length) { + getNuxtChildComponents($child, $components) + } + }) + + return $components +} + function hotReloadAPI (_app) { if (!module.hot) return - let $components = [] - let $nuxt = _app.$nuxt - while ($nuxt && $nuxt.$children && $nuxt.$children.length) { - $nuxt.$children.forEach(function (child, i) { - if (child.$vnode.data.nuxtChild) { - let hasAlready = false - $components.forEach(function (component) { - if (component.$options.__file === child.$options.__file) { - hasAlready = true - } - }) - if (!hasAlready) { - $components.push(child) - } - } - $nuxt = child - }) - } + + let $components = getNuxtChildComponents(_app.$nuxt, []) + $components.forEach(addHotReload.bind(_app)) } function addHotReload ($component, depth) { if ($component.$vnode.data._hasHotReload) return $component.$vnode.data._hasHotReload = true + var _forceUpdate = $component.$forceUpdate.bind($component.$parent) - $component.$vnode.context.$forceUpdate = () => { + + $component.$vnode.context.$forceUpdate = async () => { let Components = getMatchedComponents(router.currentRoute) let Component = Components[depth] if (!Component) return _forceUpdate() @@ -270,7 +465,12 @@ function addHotReload ($component, depth) { <%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %> router.push(path) } - let context = getContext({ route: router.currentRoute<%= (store ? ', store' : '') %>, isClient: true, hotReload: true, next: next.bind(this), error: this.error }, app) + await setContext(app, { + route: router.currentRoute, + isHMR: true, + next: next.bind(this) + }) + const context = app.context <%= (loading ? 'this.$loading.start && this.$loading.start()' : '') %> callMiddleware.call(this, Components, context) .then(() => { @@ -314,104 +514,77 @@ function addHotReload ($component, depth) { }) } } +<% } %> -// Load vue app -const NUXT = window.__NUXT__ || {} -if (!NUXT) { - throw new Error('[nuxt.js] cannot find the global variable __NUXT__, make sure the server is working.') -} -// Get matched components -const resolveComponents = function (router) { - const path = getLocation(router.options.base) - return flatMapComponents(router.match(path), (Component, _, match, key, index) => { - if (typeof Component === 'function' && !Component.options) { - return new Promise(function (resolve, reject) { - const _resolve = (Component) => { - Component = sanitizeComponent(Component) - if (NUXT.serverRendered) { - applyAsyncData(Component, NUXT.data[index]) - } - match.components[key] = Component - resolve(Component) - } - Component().then(_resolve).catch(reject) - }) - } - Component = sanitizeComponent(Component) - match.components[key] = Component - return Component - }) -} - -function nuxtReady (app) { - window._nuxtReadyCbs.forEach((cb) => { - if (typeof cb === 'function') { - cb(app) - } - }) - // Special JSDOM - if (typeof window._onNuxtLoaded === 'function') { - window._onNuxtLoaded(app) - } - // Add router hooks - router.afterEach(function (to, from) { - app.$nuxt.$emit('routeChanged', to, from) - }) -} - -createApp() -.then(async (__app) => { +async function mountApp(__app) { + // Set global variables app = __app.app router = __app.router - <%= (store ? 'store = __app.store' : '') %> + <% if (store) { %>store = __app.store <% } %> + + // Resolve route components const Components = await Promise.all(resolveComponents(router)) + + // Create Vue instance const _app = new Vue(app) + + <% if (mode !== 'spa') { %> + // Load layout const layout = NUXT.layout || 'default' await _app.loadLayout(layout) _app.setLayout(layout) - const mountApp = () => { + <% } %> + + // Mounts Vue app to DOM element + const mount = () => { _app.$mount('#__nuxt') + + // Listen for first Vue update Vue.nextTick(() => { - // Hot reloading - hotReloadAPI(_app) // Call window.onNuxtReady callbacks nuxtReady(_app) + <% if (isDev) { %> + // Enable hot reloading + hotReloadAPI(_app) + <% } %> }) } - _app.setTransitions = _app.$options._nuxt.setTransitions.bind(_app) + + // Enable transitions + _app.setTransitions = _app.$options.nuxt.setTransitions.bind(_app) if (Components.length) { _app.setTransitions(mapTransitions(Components, router.currentRoute)) - _lastPaths = router.currentRoute.matched.map((route) => compile(route.path)(router.currentRoute.params)) - _lastComponentsFiles = Components.map((Component) => Component.options.__file) + _lastPaths = router.currentRoute.matched.map(route => compile(route.path)(router.currentRoute.params)) } - _app.error = _app.$options._nuxt.error.bind(_app) - _app.$loading = {} // to avoid error while _app.$nuxt does not exist + + // Initialize error handler + _app.$loading = {} // To avoid error while _app.$nuxt does not exist if (NUXT.error) _app.error(NUXT.error) + // Add router hooks router.beforeEach(loadAsyncComponents.bind(_app)) router.beforeEach(render.bind(_app)) router.afterEach(normalizeComponents) router.afterEach(fixPrepatch.bind(_app)) + + // If page already is server rendered if (NUXT.serverRendered) { - mountApp() + mount() return } - render.call(_app, router.currentRoute, router.currentRoute, function (path) { - if (path) { - let mounted = false - router.afterEach(function () { - if (mounted) return - mounted = true - mountApp() - }) - router.push(path) + + // First render on client-side + render.call(_app, router.currentRoute, router.currentRoute, (path) => { + // If not redirected + if (!path) { + normalizeComponents(router.currentRoute, router.currentRoute) + showNextPage.call(_app, router.currentRoute) + // Dont call fixPrepatch.call(_app, router.currentRoute, router.currentRoute) since it's first render + mount() return } - normalizeComponents(router.currentRoute, router.currentRoute) - fixPrepatch.call(_app, router.currentRoute, router.currentRoute) - mountApp() + + // Push the path and then mount app + router.push(path, () => mount(), (err) => console.error(err)) }) -}) -.catch((err) => { - console.error('[nuxt.js] Cannot load components', err) // eslint-disable-line no-console -}) +} diff --git a/lib/app/components/no-ssr.js b/lib/app/components/no-ssr.js new file mode 100644 index 0000000000..9dbc5bcbf5 --- /dev/null +++ b/lib/app/components/no-ssr.js @@ -0,0 +1,28 @@ +/* +** From https://github.com/egoist/vue-no-ssr +** With the authorization of @egoist +*/ +export default { + name: 'no-ssr', + props: ['placeholder'], + data () { + return { + canRender: false + } + }, + mounted () { + this.canRender = true + }, + render (h) { + if (this.canRender) { + if ( + process.env.NODE_ENV === 'development' && + this.$slots.default.length > 1 + ) { + throw new Error(' You cannot use multiple child components') + } + return this.$slots.default[0] + } + return h('div', { class: { 'no-ssr-placeholder': true } }, this.placeholder) + } +} diff --git a/lib/app/components/nuxt-child.js b/lib/app/components/nuxt-child.js index becc3b82a8..52b5c2a78b 100644 --- a/lib/app/components/nuxt-child.js +++ b/lib/app/components/nuxt-child.js @@ -1,38 +1,12 @@ -import Vue from 'vue' - -const transitionsKeys = [ - 'name', - 'mode', - 'css', - 'type', - 'duration', - 'enterClass', - 'leaveClass', - 'enterActiveClass', - 'enterActiveClass', - 'leaveActiveClass', - 'enterToClass', - 'leaveToClass' -] -const listenersKeys = [ - 'beforeEnter', - 'enter', - 'afterEnter', - 'enterCancelled', - 'beforeLeave', - 'leave', - 'afterLeave', - 'leaveCancelled' -] - export default { name: 'nuxt-child', functional: true, render (h, { parent, data }) { data.nuxtChild = true - + const _parent = parent const transitions = parent.$nuxt.nuxt.transitions const defaultTransition = parent.$nuxt.nuxt.defaultTransition + let depth = 0 while (parent) { if (parent.$vnode && parent.$vnode.data.nuxtChild) { @@ -51,9 +25,10 @@ export default { let listeners = {} listenersKeys.forEach((key) => { if (typeof transition[key] === 'function') { - listeners[key] = transition[key] + listeners[key] = transition[key].bind(_parent) } }) + return h('transition', { props: transitionProps, on: listeners @@ -62,3 +37,37 @@ export default { ]) } } + +const transitionsKeys = [ + 'name', + 'mode', + 'appear', + 'css', + 'type', + 'duration', + 'enterClass', + 'leaveClass', + 'appearClass', + 'enterActiveClass', + 'enterActiveClass', + 'leaveActiveClass', + 'appearActiveClass', + 'enterToClass', + 'leaveToClass', + 'appearToClass' +] + +const listenersKeys = [ + 'beforeEnter', + 'enter', + 'afterEnter', + 'enterCancelled', + 'beforeLeave', + 'leave', + 'afterLeave', + 'leaveCancelled', + 'beforeAppear', + 'appear', + 'afterAppear', + 'appearCancelled' +] diff --git a/lib/app/components/nuxt-error.vue b/lib/app/components/nuxt-error.vue index 476d67d441..a304f59e85 100644 --- a/lib/app/components/nuxt-error.vue +++ b/lib/app/components/nuxt-error.vue @@ -1,13 +1,21 @@ - diff --git a/lib/app/components/nuxt-link.js b/lib/app/components/nuxt-link.js index c23fdd7ac3..fea5f308ce 100644 --- a/lib/app/components/nuxt-link.js +++ b/lib/app/components/nuxt-link.js @@ -1,5 +1,3 @@ -import Vue from 'vue' - export default { name: 'nuxt-link', functional: true, diff --git a/lib/app/components/nuxt-loading.vue b/lib/app/components/nuxt-loading.vue index f7d0e91cae..349a1e60e0 100644 --- a/lib/app/components/nuxt-loading.vue +++ b/lib/app/components/nuxt-loading.vue @@ -1,5 +1,5 @@ + + \ No newline at end of file diff --git a/test/fixtures/with-config/layouts/desktop/default.vue b/test/fixtures/with-config/layouts/desktop/default.vue new file mode 100644 index 0000000000..329274e836 --- /dev/null +++ b/test/fixtures/with-config/layouts/desktop/default.vue @@ -0,0 +1,20 @@ + + + diff --git a/test/fixtures/with-config/layouts/mobile/default.vue b/test/fixtures/with-config/layouts/mobile/default.vue new file mode 100644 index 0000000000..548fe9b27b --- /dev/null +++ b/test/fixtures/with-config/layouts/mobile/default.vue @@ -0,0 +1,21 @@ + + + + diff --git a/test/fixtures/with-config/middleware/noop.js b/test/fixtures/with-config/middleware/noop.js index 26f7210f3d..335d554e10 100644 --- a/test/fixtures/with-config/middleware/noop.js +++ b/test/fixtures/with-config/middleware/noop.js @@ -1,3 +1,3 @@ export default function () { - // NOOP! -} \ No newline at end of file + // NOOP! +} diff --git a/test/fixtures/with-config/middleware/user-agent.js b/test/fixtures/with-config/middleware/user-agent.js index 097436a73d..662a76ef02 100644 --- a/test/fixtures/with-config/middleware/user-agent.js +++ b/test/fixtures/with-config/middleware/user-agent.js @@ -1,3 +1,3 @@ export default function (context) { - context.userAgent = context.isServer ? context.req.headers['user-agent'] : navigator.userAgent + context.userAgent = process.server ? context.req.headers['user-agent'] : navigator.userAgent } diff --git a/test/fixtures/with-config/nuxt.config.js b/test/fixtures/with-config/nuxt.config.js index f9e56aa032..34012a7fc1 100644 --- a/test/fixtures/with-config/nuxt.config.js +++ b/test/fixtures/with-config/nuxt.config.js @@ -1,44 +1,68 @@ +const path = require('path') + module.exports = { srcDir: __dirname, router: { base: '/test/', middleware: 'noop', - extendRoutes (routes) { - routes.push({ - name: 'about-bis', - path: '/about-bis', - component: '~pages/about.vue' - }) + extendRoutes(routes) { + return [ + ...routes, + { + name: 'about-bis', + path: '/about-bis', + component: '~/pages/about.vue' + } + ] } }, + modulesDir: path.join(__dirname, '..', '..', '..', 'node_modules'), transition: 'test', + layoutTransition: 'test', offline: true, plugins: [ - '~plugins/test.js', - { src: '~plugins/offline.js', ssr: false }, - { src: '~plugins/only-client.js', ssr: false } + '~/plugins/test.js', + { src: '~/plugins/only-client.js', ssr: false } ], - loading: '~components/loading', + loading: '~/components/loading', env: { bool: true, num: 23, - string: 'Nuxt.js' + string: 'Nuxt.js', + object: { + bool: false, + string: 'ok', + num2: 8.23, + obj: { + again: true + } + } }, build: { - extractCSS: true, + // extractCSS: true, publicPath: '/orion/', analyze: { analyzerMode: 'disabled', generateStatsFile: true }, - extend (config, options) { - config.devtool = 'nosources-source-map' + extend(config, options) { + return Object.assign({}, config, { + devtool: 'nosources-source-map' + }) } }, css: [ { src: '~/assets/app.css' } ], render: { + http2: { + push: true + }, + bundleRenderer: { + shouldPreload: (file, type) => { + return ['script', 'style', 'font'].includes(type) + } + }, static: { maxAge: '1y' } diff --git a/test/fixtures/with-config/pages/desktop.vue b/test/fixtures/with-config/pages/desktop.vue new file mode 100644 index 0000000000..4c0951062e --- /dev/null +++ b/test/fixtures/with-config/pages/desktop.vue @@ -0,0 +1,12 @@ + + + + diff --git a/test/fixtures/with-config/pages/env.vue b/test/fixtures/with-config/pages/env.vue index df9f95d882..1c2dd3419e 100644 --- a/test/fixtures/with-config/pages/env.vue +++ b/test/fixtures/with-config/pages/env.vue @@ -1,11 +1,20 @@ + diff --git a/test/fixtures/with-config/pages/user-agent.vue b/test/fixtures/with-config/pages/user-agent.vue index dff65e97d8..904156ace0 100644 --- a/test/fixtures/with-config/pages/user-agent.vue +++ b/test/fixtures/with-config/pages/user-agent.vue @@ -5,7 +5,7 @@