mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-11 08:33:53 +00:00
Merge branch 'dev' into dev
This commit is contained in:
commit
179b1a7bc1
@ -2,3 +2,4 @@ app
|
|||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
.nuxt
|
.nuxt
|
||||||
|
examples/coffeescript/pages/index.vue
|
||||||
|
72
README.md
72
README.md
@ -15,9 +15,29 @@
|
|||||||
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
> Nuxt.js is a Versatile Vue.js Framework
|
> Vue.js Meta Framework to create complex, fast & universal web application *quickly*.
|
||||||
|
|
||||||
## 🚧 Under active development, [1.0](https://github.com/nuxt/nuxt.js/projects/1) will be released soon :fire:
|
## Links
|
||||||
|
|
||||||
|
- 📘 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)
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Automatic transpilation and bundling (with webpack and babel)
|
||||||
|
- Hot code reloading
|
||||||
|
- Server-side rendering OR Single Page App OR Static Generated, you choose :fire:
|
||||||
|
- Static file serving. `./static/` is mapped to `/`
|
||||||
|
- Configurable with a `nuxt.config.js` file
|
||||||
|
- Custom layouts with the `layouts/` directory
|
||||||
|
- Middleware
|
||||||
|
- Code splitting for every `pages/`
|
||||||
|
|
||||||
|
Learn more at [nuxtjs.org](https://nuxtjs.org).
|
||||||
|
|
||||||
## Sponsors
|
## Sponsors
|
||||||
|
|
||||||
@ -93,15 +113,6 @@ Support us with a monthly donation and help us continue our activities. [[Become
|
|||||||
<a href="https://opencollective.com/nuxtjs/backer/29/website" target="_blank" rel="noopener noreferrer"><img src="https://opencollective.com/nuxtjs/backer/29/avatar.svg"></a>
|
<a href="https://opencollective.com/nuxtjs/backer/29/website" target="_blank" rel="noopener noreferrer"><img src="https://opencollective.com/nuxtjs/backer/29/avatar.svg"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Links
|
|
||||||
|
|
||||||
- 📘 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
|
## Getting started
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -143,22 +154,11 @@ npm start
|
|||||||
|
|
||||||
Go to [http://localhost:3000](http://localhost:3000)
|
Go to [http://localhost:3000](http://localhost:3000)
|
||||||
|
|
||||||
So far, we get:
|
|
||||||
|
|
||||||
- Automatic transpilation and bundling (with webpack and babel)
|
|
||||||
- Hot code reloading
|
|
||||||
- Server rendering and indexing of `pages/`
|
|
||||||
- Static file serving. `./static/` is mapped to `/`
|
|
||||||
- Configurable with a `nuxt.config.js` file
|
|
||||||
- Custom layouts with the `layouts/` directory
|
|
||||||
- Middleware
|
|
||||||
- Code splitting via webpack
|
|
||||||
|
|
||||||
Learn more at [nuxtjs.org](https://nuxtjs.org).
|
|
||||||
|
|
||||||
## Templates
|
## Templates
|
||||||
|
|
||||||
You can start by using one of our starter templates:
|
:point_right: We recommend to start directly with our cli [create-nuxt-app](https://github.com/nuxt-community/create-nuxt-app) for the lastest updates.
|
||||||
|
|
||||||
|
Or you can start by using one of our starter templates:
|
||||||
- [starter](https://github.com/nuxt-community/starter-template): Basic Nuxt.js project template
|
- [starter](https://github.com/nuxt-community/starter-template): Basic Nuxt.js project template
|
||||||
- [express](https://github.com/nuxt-community/express-template): Nuxt.js + Express
|
- [express](https://github.com/nuxt-community/express-template): Nuxt.js + Express
|
||||||
- [koa](https://github.com/nuxt-community/koa-template): Nuxt.js + Koa
|
- [koa](https://github.com/nuxt-community/koa-template): Nuxt.js + Koa
|
||||||
@ -173,7 +173,7 @@ const { Nuxt, Builder } = require('nuxt')
|
|||||||
|
|
||||||
// Import and set nuxt.js options
|
// Import and set nuxt.js options
|
||||||
let config = require('./nuxt.config.js')
|
let config = require('./nuxt.config.js')
|
||||||
config.dev = !(process.env.NODE_ENV === 'production')
|
config.dev = (process.env.NODE_ENV !== 'production')
|
||||||
|
|
||||||
let nuxt = new Nuxt(config)
|
let nuxt = new Nuxt(config)
|
||||||
|
|
||||||
@ -220,7 +220,7 @@ Learn more: https://nuxtjs.org/api/nuxt-render-route
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
Please take a look at https://nuxtjs.org/examples
|
Please take a look at https://nuxtjs.org/examples or directly in https://github.com/nuxt/nuxt.js/tree/dev/examples.
|
||||||
|
|
||||||
## Production deployment
|
## Production deployment
|
||||||
|
|
||||||
@ -249,9 +249,21 @@ Then run `now` and enjoy!
|
|||||||
|
|
||||||
Note: we recommend putting `.nuxt` in `.npmignore` or `.gitignore`.
|
Note: we recommend putting `.nuxt` in `.npmignore` or `.gitignore`.
|
||||||
|
|
||||||
|
## Core team
|
||||||
|
|
||||||
|
| [Sebastien Chopin](https://github.com/Atinux) | [Alexandre Chopin](https://github.com/alexchopin) | [Pooya Parsa](https://github.com/pi0) | [Clark Du](https://github.com/clarkdo) |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| [![Atinux](https://avatars1.githubusercontent.com/u/904724?s=150&v=4)](https://github.com/Atinux) | [![alexchopin](https://avatars1.githubusercontent.com/u/4084277?s=150&v=4)](https://github.com/alexchopin) | [![pi0](https://avatars1.githubusercontent.com/u/5158436?s=150&v=4)](https://github.com/pi0) | [![clarkdo](https://avatars3.githubusercontent.com/u/4312154?s=150&v=4)](https://github.com/clarkdo) |
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
Thank you to all our [contributors](https://github.com/nuxt/nuxt.js/graphs/contributors)!
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Please see our [CONTRIBUTING.md](./CONTRIBUTING.md)
|
||||||
|
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
https://trello.com/b/lgy93IOl/nuxtjs-10
|
https://trello.com/b/lgy93IOl/nuxtjs-10
|
||||||
|
|
||||||
## Contributing
|
|
||||||
Please see our [CONTRIBUTING.md](./CONTRIBUTING.md)
|
|
||||||
|
@ -33,7 +33,6 @@ if (argv.help) {
|
|||||||
Usage
|
Usage
|
||||||
$ nuxt generate <dir>
|
$ nuxt generate <dir>
|
||||||
Options
|
Options
|
||||||
--spa Launch in SPA mode
|
|
||||||
--spa Launch in SPA mode
|
--spa Launch in SPA mode
|
||||||
--universal Launch in Universal mode (default)
|
--universal Launch in Universal mode (default)
|
||||||
--config-file, -c Path to Nuxt.js config file (default: nuxt.config.js)
|
--config-file, -c Path to Nuxt.js config file (default: nuxt.config.js)
|
||||||
|
22
examples/coffeescript/README.md
Normal file
22
examples/coffeescript/README.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# CoffeeScript
|
||||||
|
|
||||||
|
> Nuxt.js project with CoffeeScript
|
||||||
|
|
||||||
|
## Build Setup
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
# install dependencies
|
||||||
|
$ npm install # Or yarn install
|
||||||
|
|
||||||
|
# serve with hot reload at localhost:3000
|
||||||
|
$ npm run dev
|
||||||
|
|
||||||
|
# build for production and launch server
|
||||||
|
$ npm run build
|
||||||
|
$ npm start
|
||||||
|
|
||||||
|
# generate static project
|
||||||
|
$ npm run generate
|
||||||
|
```
|
||||||
|
|
||||||
|
For detailed explanation on how things work, checkout the [Nuxt.js docs](https://github.com/nuxt/nuxt.js).
|
79
examples/coffeescript/components/Logo.vue
Normal file
79
examples/coffeescript/components/Logo.vue
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<template>
|
||||||
|
<div class="VueToNuxtLogo">
|
||||||
|
<div class="Triangle Triangle--two"></div>
|
||||||
|
<div class="Triangle Triangle--one"></div>
|
||||||
|
<div class="Triangle Triangle--three"></div>
|
||||||
|
<div class="Triangle Triangle--four"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.VueToNuxtLogo {
|
||||||
|
display: inline-block;
|
||||||
|
animation: turn 2s linear forwards 1s;
|
||||||
|
transform: rotateX(180deg);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 180px;
|
||||||
|
width: 245px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Triangle {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Triangle--one {
|
||||||
|
border-left: 105px solid transparent;
|
||||||
|
border-right: 105px solid transparent;
|
||||||
|
border-bottom: 180px solid #41B883;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Triangle--two {
|
||||||
|
top: 30px;
|
||||||
|
left: 35px;
|
||||||
|
animation: goright 0.5s linear forwards 3.5s;
|
||||||
|
border-left: 87.5px solid transparent;
|
||||||
|
border-right: 87.5px solid transparent;
|
||||||
|
border-bottom: 150px solid #3B8070;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Triangle--three {
|
||||||
|
top: 60px;
|
||||||
|
left: 35px;
|
||||||
|
animation: goright 0.5s linear forwards 3.5s;
|
||||||
|
border-left: 70px solid transparent;
|
||||||
|
border-right: 70px solid transparent;
|
||||||
|
border-bottom: 120px solid #35495E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Triangle--four {
|
||||||
|
top: 120px;
|
||||||
|
left: 70px;
|
||||||
|
animation: godown 0.5s linear forwards 3s;
|
||||||
|
border-left: 35px solid transparent;
|
||||||
|
border-right: 35px solid transparent;
|
||||||
|
border-bottom: 60px solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes turn {
|
||||||
|
100% {
|
||||||
|
transform: rotateX(0deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes godown {
|
||||||
|
100% {
|
||||||
|
top: 180px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes goright {
|
||||||
|
100% {
|
||||||
|
left: 70px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
52
examples/coffeescript/layouts/default.vue
Normal file
52
examples/coffeescript/layouts/default.vue
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<nuxt/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html {
|
||||||
|
font-family: "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
word-spacing: 1px;
|
||||||
|
-ms-text-size-adjust: 100%;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
*, *:before, *:after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button--green {
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #3b8070;
|
||||||
|
color: #3b8070;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 10px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button--green:hover {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #3b8070;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button--grey {
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #35495e;
|
||||||
|
color: #35495e;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 10px 30px;
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button--grey:hover {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #35495e;
|
||||||
|
}
|
||||||
|
</style>
|
23
examples/coffeescript/modules/coffeescript.js
Normal file
23
examples/coffeescript/modules/coffeescript.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
module.exports = function () {
|
||||||
|
// Add .coffee extension for store, middleware and more
|
||||||
|
this.nuxt.options.extensions.push('coffee')
|
||||||
|
// Extend build
|
||||||
|
const coffeeLoader = {
|
||||||
|
test: /\.coffee$/,
|
||||||
|
loader: 'coffee-loader'
|
||||||
|
}
|
||||||
|
this.extendBuild(config => {
|
||||||
|
// Add CoffeeScruot loader
|
||||||
|
config.module.rules.push(coffeeLoader)
|
||||||
|
// Add CoffeeScript loader for vue files
|
||||||
|
for (let rule of config.module.rules) {
|
||||||
|
if (rule.loader === 'vue-loader') {
|
||||||
|
rule.options.loaders.coffee = coffeeLoader
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add .coffee extension in webpack resolve
|
||||||
|
if (config.resolve.extensions.indexOf('.coffee') === -1) {
|
||||||
|
config.resolve.extensions.push('.coffee')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
24
examples/coffeescript/nuxt.config.js
Normal file
24
examples/coffeescript/nuxt.config.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
module.exports = {
|
||||||
|
/*
|
||||||
|
** Headers of the page
|
||||||
|
*/
|
||||||
|
head: {
|
||||||
|
title: 'Nuxt with CoffeeScript',
|
||||||
|
meta: [
|
||||||
|
{ charset: 'utf-8' },
|
||||||
|
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||||
|
{ hid: 'description', name: 'description', content: 'Nuxt.js project' }
|
||||||
|
],
|
||||||
|
link: [
|
||||||
|
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
** Customize the progress bar color
|
||||||
|
*/
|
||||||
|
loading: { color: '#3B8070' },
|
||||||
|
/*
|
||||||
|
** Modules
|
||||||
|
*/
|
||||||
|
modules: ['~/modules/coffeescript']
|
||||||
|
}
|
19
examples/coffeescript/package.json
Normal file
19
examples/coffeescript/package.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "coffeescript",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Nuxt.js with CoffeeScript",
|
||||||
|
"author": "Alex Ananiev <alex.ananiev.r@gmail.com>",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nuxt",
|
||||||
|
"build": "nuxt build",
|
||||||
|
"start": "nuxt start"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"nuxt": "latest"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"coffee-loader": "^0.8.0",
|
||||||
|
"coffeescript": "^2.0.1"
|
||||||
|
}
|
||||||
|
}
|
7
examples/coffeescript/pages/README.md
Normal file
7
examples/coffeescript/pages/README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# PAGES
|
||||||
|
|
||||||
|
This directory contains your Application Views and Routes.
|
||||||
|
The framework reads all the .vue files inside this directory and create the router of your application.
|
||||||
|
|
||||||
|
More information about the usage of this directory in the documentation:
|
||||||
|
https://nuxtjs.org/guide/routing
|
58
examples/coffeescript/pages/index.vue
Normal file
58
examples/coffeescript/pages/index.vue
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<section class="container">
|
||||||
|
<div>
|
||||||
|
<logo/>
|
||||||
|
<h1 class="title">
|
||||||
|
Nuxt.js with Coffee!
|
||||||
|
</h1>
|
||||||
|
<h2 class="subtitle">
|
||||||
|
{{message}}
|
||||||
|
</h2>
|
||||||
|
<div class="links">
|
||||||
|
<a href="https://nuxtjs.org/" target="_blank" class="button--green">Documentation</a>
|
||||||
|
<a href="https://github.com/nuxt/nuxt.js" target="_blank" class="button--grey">GitHub</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="coffee">
|
||||||
|
import Logo from '~/components/Logo.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { Logo }
|
||||||
|
computed:
|
||||||
|
message: -> @$store.state.message
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-family: "Quicksand", "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; /* 1 */
|
||||||
|
display: block;
|
||||||
|
font-weight: 300;
|
||||||
|
font-size: 100px;
|
||||||
|
color: #35495e;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-weight: 300;
|
||||||
|
font-size: 42px;
|
||||||
|
color: #526488;
|
||||||
|
word-spacing: 5px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.links {
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
</style>
|
BIN
examples/coffeescript/static/favicon.ico
Normal file
BIN
examples/coffeescript/static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
2
examples/coffeescript/store/index.coffee
Normal file
2
examples/coffeescript/store/index.coffee
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export state = ->
|
||||||
|
message: 'Hello CoffeeScript!'
|
@ -7,16 +7,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
// **PLEASE NOTE** All "Nuxt Class Components" require at minimum a script tag that exports a default object
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
// PLEASE NOTE
|
||||||
|
// All "Nuxt Class Components" require at minimum a script tag that exports a default object
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Component from 'nuxt-class-component'
|
import Component from 'nuxt-class-component'
|
||||||
import { Prop } from 'vue-property-decorator'
|
import { Prop } from 'vue-property-decorator'
|
||||||
import { Action } from 'vuex-class'
|
import { Action, namespace } from 'vuex-class'
|
||||||
|
|
||||||
|
import * as people from '~/store/modules/people'
|
||||||
|
|
||||||
|
const PeopleAction = namespace(people.name, Action)
|
||||||
|
|
||||||
@Component({})
|
@Component({})
|
||||||
export default class Card extends Vue {
|
export default class Card extends Vue {
|
||||||
@Prop() person
|
@Prop() person
|
||||||
@Action select
|
@PeopleAction select
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
@ -1,4 +1,6 @@
|
|||||||
module.exports = function (options) {
|
module.exports = function () {
|
||||||
|
// Add .ts extension for store, middleware and more
|
||||||
|
this.nuxt.options.extensions.push('ts')
|
||||||
// Extend build
|
// Extend build
|
||||||
this.extendBuild(config => {
|
this.extendBuild(config => {
|
||||||
const tsLoader = {
|
const tsLoader = {
|
||||||
@ -22,5 +24,9 @@ module.exports = function (options) {
|
|||||||
rule.options.loaders.ts = tsLoader
|
rule.options.loaders.ts = tsLoader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Add .ts extension in webpack resolve
|
||||||
|
if (config.resolve.extensions.indexOf('.ts') === -1) {
|
||||||
|
config.resolve.extensions.push('.ts')
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
css: ['tachyons/css/tachyons.min.css', '~/assets/css/main.css'],
|
css: ['tachyons/css/tachyons.min.css', '~/assets/css/main.css'],
|
||||||
build: {
|
build: {
|
||||||
vendor: ['axios', 'gsap', 'vuex-class', 'nuxt-class-component']
|
vendor: ['axios', 'vuex-class', 'nuxt-class-component']
|
||||||
},
|
},
|
||||||
modules: ['~/modules/typescript']
|
modules: ['~/modules/typescript']
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,10 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.16.1",
|
"axios": "^0.17.1",
|
||||||
"gsap": "^1.19.1",
|
|
||||||
"nuxt": "latest",
|
"nuxt": "latest",
|
||||||
"nuxt-class-component": "^1.0.3",
|
"nuxt-class-component": "^1.1.3",
|
||||||
"tachyons": "^4.7.0",
|
"tachyons": "^4.9.1",
|
||||||
"vue": "~2.5.1",
|
|
||||||
"vue-server-renderer": "~2.5.1",
|
|
||||||
"vue-template-compiler": "~2.5.1",
|
|
||||||
"vue-class-component": "^6.0.0",
|
|
||||||
"vue-property-decorator": "^6.0.0",
|
"vue-property-decorator": "^6.0.0",
|
||||||
"vuex-class": "^0.3.0"
|
"vuex-class": "^0.3.0"
|
||||||
},
|
},
|
||||||
@ -21,7 +16,7 @@
|
|||||||
"generate": "nuxt generate"
|
"generate": "nuxt generate"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ts-loader": "^3.0.0",
|
"ts-loader": "^3.2.0",
|
||||||
"typescript": "^2.2.2"
|
"typescript": "^2.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Component from 'nuxt-class-component'
|
import Component from 'nuxt-class-component'
|
||||||
import Card from '~/components/Card'
|
import Card from '~/components/Card.vue'
|
||||||
import { State, Getter } from 'vuex-class'
|
import { State, Getter, namespace } from 'vuex-class'
|
||||||
|
|
||||||
|
import * as people from '~/store/modules/people'
|
||||||
|
|
||||||
|
const PeopleState = namespace(people.name, State)
|
||||||
|
const PeopleGetter = namespace(people.name, Getter)
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
@ -25,8 +30,8 @@ import { State, Getter } from 'vuex-class'
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
export default class extends Vue {
|
export default class extends Vue {
|
||||||
@State selected
|
@PeopleState selected
|
||||||
@State people
|
@PeopleState people
|
||||||
@Getter selectedPerson
|
@PeopleGetter selectedPerson
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,33 +1,32 @@
|
|||||||
import axios from "~/plugins/axios";
|
import Vuex from 'vuex'
|
||||||
|
import * as root from './root'
|
||||||
|
import * as people from './modules/people'
|
||||||
|
|
||||||
export const state = () => ({
|
// More info about store: https://vuex.vuejs.org/en/core-concepts.html
|
||||||
selected: 1,
|
// See https://nuxtjs.org/guide/vuex-store#classic-mode
|
||||||
people: []
|
// structure of the store:
|
||||||
});
|
// types: Types that represent the keys of the mutations to commit
|
||||||
|
// state: The information of our app, we can get or update it.
|
||||||
|
// getters: Get complex information from state
|
||||||
|
// action: Sync or async operations that commit mutations
|
||||||
|
// mutations: Modify the state
|
||||||
|
|
||||||
export const mutations = {
|
interface ModulesStates {
|
||||||
select(state, id) {
|
people: people.State
|
||||||
state.selected = id;
|
}
|
||||||
},
|
|
||||||
setPeople(state, people) {
|
|
||||||
state.people = people;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getters = {
|
export type RootState = root.State & ModulesStates
|
||||||
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 = {
|
const createStore = () => {
|
||||||
async nuxtServerInit({ commit }) {
|
return new Vuex.Store({
|
||||||
const response = await axios.get("/random-data.json");
|
state: root.state(),
|
||||||
const people = response.data.slice(0, 10);
|
getters: root.getters,
|
||||||
commit("setPeople", people);
|
mutations: root.mutations,
|
||||||
},
|
actions: root.actions,
|
||||||
select({ commit }, id) {
|
modules: {
|
||||||
commit("select", id);
|
[people.name]: people
|
||||||
}
|
}
|
||||||
};
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default createStore
|
||||||
|
71
examples/typescript/store/modules/people.ts
Normal file
71
examples/typescript/store/modules/people.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { ActionTree, MutationTree, GetterTree, ActionContext } from 'vuex'
|
||||||
|
import { RootState } from 'store'
|
||||||
|
|
||||||
|
export const name = 'people'
|
||||||
|
|
||||||
|
export const types = {
|
||||||
|
SELECT: 'SELECT',
|
||||||
|
SET: 'SET'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PersonContact {
|
||||||
|
email: string
|
||||||
|
phone: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PersonAddress {
|
||||||
|
city: string
|
||||||
|
country: string
|
||||||
|
postalCode: string
|
||||||
|
state: string
|
||||||
|
street: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Person {
|
||||||
|
id: number
|
||||||
|
first_name: string
|
||||||
|
last_name: string
|
||||||
|
contact: PersonContact
|
||||||
|
gender: string
|
||||||
|
ip_address: string
|
||||||
|
avatar: string
|
||||||
|
addres: PersonAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
selected: number
|
||||||
|
people: Person[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const namespaced = true
|
||||||
|
|
||||||
|
export const state = (): State => ({
|
||||||
|
selected: 1,
|
||||||
|
people: []
|
||||||
|
})
|
||||||
|
|
||||||
|
export const getters: GetterTree<State, RootState> = {
|
||||||
|
selectedPerson: state => {
|
||||||
|
const p = state.people.find(person => person.id === state.selected)
|
||||||
|
return p ? p : { first_name: 'Please,', last_name: 'select someone' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Actions<S, R> extends ActionTree<S, R> {
|
||||||
|
select(context: ActionContext<S, R>, id: number): void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const actions: Actions<State, RootState> = {
|
||||||
|
select({ commit }, id: number) {
|
||||||
|
commit(types.SELECT, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mutations: MutationTree<State> = {
|
||||||
|
[types.SELECT](state, id: number) {
|
||||||
|
state.selected = id
|
||||||
|
},
|
||||||
|
[types.SET](state, people: Person[]) {
|
||||||
|
state.people = people
|
||||||
|
}
|
||||||
|
}
|
26
examples/typescript/store/root.ts
Normal file
26
examples/typescript/store/root.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { GetterTree, ActionContext, ActionTree, MutationTree } from 'vuex'
|
||||||
|
import axios from '~/plugins/axios'
|
||||||
|
import { RootState } from 'store'
|
||||||
|
import * as people from './modules/people'
|
||||||
|
|
||||||
|
export const types = {}
|
||||||
|
|
||||||
|
export interface State {}
|
||||||
|
|
||||||
|
export const state = (): State => ({})
|
||||||
|
|
||||||
|
export const getters: GetterTree<State, RootState> = {}
|
||||||
|
|
||||||
|
export interface Actions<S, R> extends ActionTree<S, R> {
|
||||||
|
nuxtServerInit(context: ActionContext<S, R>): void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const actions: Actions<State, RootState> = {
|
||||||
|
async nuxtServerInit({ commit }) {
|
||||||
|
const response = await axios.get('/random-data.json')
|
||||||
|
const staticPeople = response.data.slice(0, 10)
|
||||||
|
commit(`${people.name}/${people.types.SET}`, staticPeople, { root: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mutations: MutationTree<State> = {}
|
@ -23,7 +23,8 @@
|
|||||||
"~/middleware/*": ["./middleware/*"],
|
"~/middleware/*": ["./middleware/*"],
|
||||||
"~/pages/*": ["./pages/*"],
|
"~/pages/*": ["./pages/*"],
|
||||||
"~/plugins/*": ["./plugins/*"],
|
"~/plugins/*": ["./plugins/*"],
|
||||||
"~/static/*": ["./static/*"]
|
"~/static/*": ["./static/*"],
|
||||||
|
"~/store/*": ["./store/*"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
examples/with-amp/app.html
Normal file
11
examples/with-amp/app.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html ⚡ {{ HTML_ATTRS }}>
|
||||||
|
<head>
|
||||||
|
{{ HEAD }}
|
||||||
|
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style>
|
||||||
|
<noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
|
||||||
|
</head>
|
||||||
|
<body {{ BODY_ATTRS }}>
|
||||||
|
{{ APP }}
|
||||||
|
</body>
|
||||||
|
</html>
|
23
examples/with-amp/assets/main.css
Normal file
23
examples/with-amp/assets/main.css
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
body {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
padding: 30px;
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.caption {
|
||||||
|
color: #ccc;
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.byline {
|
||||||
|
color: #aaa;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 30px;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
11
examples/with-amp/components/Byline.vue
Normal file
11
examples/with-amp/components/Byline.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<template>
|
||||||
|
<div class="byline">
|
||||||
|
By {{ author }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['author']
|
||||||
|
}
|
||||||
|
</script>
|
33
examples/with-amp/nuxt.config.js
Normal file
33
examples/with-amp/nuxt.config.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
module.exports = {
|
||||||
|
head: {
|
||||||
|
meta: [
|
||||||
|
{ charset: 'utf-8' },
|
||||||
|
{ name: 'viewport', content: 'width=device-width,minimum-scale=1' }
|
||||||
|
],
|
||||||
|
link: [
|
||||||
|
{ rel: 'canonical', href: '/' },
|
||||||
|
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css?family=Roboto' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
css: ['~/assets/main.css'],
|
||||||
|
loading: false, // Disable loading bar since AMP will not generate a dynamic page
|
||||||
|
render: {
|
||||||
|
// Disable resourceHints since we don't load any scripts for AMP
|
||||||
|
resourceHints: false
|
||||||
|
},
|
||||||
|
hooks: {
|
||||||
|
// This hook is called before rendering the html to the browser
|
||||||
|
'render:route': (url, result) => {
|
||||||
|
let html = result.html
|
||||||
|
// Add amp-custom tag to added CSS
|
||||||
|
html = html.replace(/<style data-vue-ssr/g, '<style amp-custom data-vue-ssr')
|
||||||
|
// Remove every script tag from generated HTML
|
||||||
|
html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
|
||||||
|
// Add AMP script before </head>
|
||||||
|
const ampScript = '<script async src="https://cdn.ampproject.org/v0.js"></script>'
|
||||||
|
html = html.replace('</head>', ampScript + '</head>')
|
||||||
|
// Update result html
|
||||||
|
result.html = html
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
examples/with-amp/package.json
Normal file
12
examples/with-amp/package.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "with-amp",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"nuxt": "latest"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nuxt",
|
||||||
|
"build": "nuxt build",
|
||||||
|
"start": "nuxt start"
|
||||||
|
}
|
||||||
|
}
|
29
examples/with-amp/pages/dog.vue
Normal file
29
examples/with-amp/pages/dog.vue
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>The Dog</h1>
|
||||||
|
<byline author="Meow Meow Fuzzyface"/>
|
||||||
|
<amp-img src="/dog.jpg" width="470" height="350" layout="responsive" alt="Woof"/>
|
||||||
|
<p class="caption">Woooooooooooof</p>
|
||||||
|
<p>Go to <a href="/">the cat</a>.</p>
|
||||||
|
<p>Wafer donut candy soufflé lemon drops icing. Marzipan gummi bears pie danish lollipop pudding powder gummi bears sweet. Pie sweet roll sweet roll topping chocolate bar dragée pudding chocolate cake. Croissant sweet chocolate bar cheesecake candy canes. Tootsie roll icing macaroon bonbon cupcake apple pie candy canes biscuit candy canes. Jujubes jelly liquorice toffee gingerbread. Candy tootsie roll macaroon chocolate bar icing sugar plum pie. Icing gummies chocolate bar chocolate marzipan bonbon cookie chocolate tart. Caramels danish halvah croissant. Cheesecake cookie tootsie roll ice cream. Powder dessert carrot cake muffin tiramisu lemon drops liquorice topping brownie. Soufflé chocolate cake croissant cupcake jelly.</p>
|
||||||
|
<p>Muffin gummies dessert cheesecake candy canes. Candy canes danish cotton candy tart dessert powder bear claw marshmallow. Muffin chocolate marshmallow danish. Chocolate bar biscuit cake tiramisu. Topping sweet brownie jujubes powder marzipan. Croissant wafer bonbon chupa chups cake cake marzipan caramels jujubes. Cupcake cheesecake sweet roll marshmallow lollipop danish jujubes jelly icing. Apple pie chupa chups lollipop jelly-o cheesecake jelly beans cake dessert. Tootsie roll tootsie roll bonbon pastry croissant gummi bears cake cake. Fruitcake sugar plum halvah gingerbread cookie pastry chupa chups wafer lemon drops. Marshmallow liquorice oat cake lollipop. Lemon drops oat cake halvah liquorice danish powder cupcake soufflé. Cake tart topping jelly-o tart sugar plum. Chocolate bar cookie wafer tootsie roll candy cotton candy toffee pie donut.</p>
|
||||||
|
<p>Ice cream lollipop marshmallow tiramisu jujubes croissant. Bear claw lemon drops marzipan candy bonbon cupcake powder. Candy canes cheesecake bear claw pastry cake donut jujubes. Icing tart jelly-o soufflé bonbon apple pie. Cheesecake pie chupa chups toffee powder. Bonbon lemon drops carrot cake pudding candy halvah cheesecake lollipop cupcake. Pudding marshmallow fruitcake. Gummi bears bonbon chupa chups lemon drops. Wafer dessert gummies gummi bears biscuit donut tiramisu gummi bears brownie. Tootsie roll liquorice bonbon cookie. Sesame snaps chocolate bar cake croissant chupa chups cheesecake gingerbread tiramisu jelly. Cheesecake ice cream muffin lollipop gummies. Sesame snaps jelly beans sweet bear claw tart.</p>
|
||||||
|
<p>Sweet topping chupa chups chocolate cake jelly-o liquorice danish. Pastry jelly beans apple pie dessert pastry lemon drops marzipan gummies. Jelly beans macaroon bear claw cotton candy. Toffee sweet lollipop toffee oat cake. Jelly-o oat cake fruitcake chocolate bar sweet. Lemon drops gummies chocolate cake lollipop bear claw croissant danish icing. Chocolate bar donut brownie chocolate cake lemon drops chocolate bar. Cake fruitcake pudding chocolate apple pie. Brownie tiramisu chocolate macaroon lemon drops wafer soufflé jujubes icing. Cheesecake tiramisu cake macaroon tart lollipop donut. Gummi bears dragée pudding bear claw. Muffin cake cupcake candy canes. Soufflé candy canes biscuit. Macaroon gummies danish.</p>
|
||||||
|
<p>Cupcake cupcake tart. Cotton candy danish candy canes oat cake ice cream candy canes powder wafer. Chocolate sesame snaps oat cake dragée cheesecake. Sesame snaps marshmallow topping liquorice cookie marshmallow. Liquorice pudding chocolate bar. Cake powder brownie fruitcake. Carrot cake dessert marzipan sugar plum cupcake cheesecake pastry. Apple pie macaroon ice cream fruitcake apple pie cookie. Tootsie roll ice cream oat cake cheesecake donut cheesecake bear claw. Sesame snaps marzipan jelly beans chocolate tootsie roll. Chocolate bar donut dragée ice cream biscuit. Pie candy canes muffin candy canes ice cream tiramisu.</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Byline from '../components/Byline'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
head: {
|
||||||
|
title: 'The Dog'
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Byline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- You should not write any CSS here, but use ~/assets/main.css instead -->
|
29
examples/with-amp/pages/index.vue
Normal file
29
examples/with-amp/pages/index.vue
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>The Cat</h1>
|
||||||
|
<byline author="Dan Zajdband"/>
|
||||||
|
<amp-img src="/cat.jpg" width="470" height="350" layout="responsive" alt="Meow"/>
|
||||||
|
<p class="caption">Meowwwwwwww</p>
|
||||||
|
<p>Go to <a href="/dog">the dog</a>.</p>
|
||||||
|
<p>Cat ipsum dolor sit amet, eat grass, throw it back up but refuse to leave cardboard box or groom yourself 4 hours - checked, have your beauty sleep 18 hours - checked, be fabulous for the rest of the day - checked!. Hide from vacuum cleaner. Chirp at birds chew foot chase the pig around the house and meoooow!. Chase ball of string climb a tree, wait for a fireman jump to fireman then scratch his face claw drapes, for meow to be let in yet attack dog, run away and pretend to be victim meow loudly just to annoy owners. Touch water with paw then recoil in horror hide when guests come over, and tuxedo cats always looking dapper so has closed eyes but still sees you or a nice warm laptop for me to sit on pee in human's bed until he cleans the litter box. Steal the warm chair right after you get up cat not kitten around yet claws in your leg eat all the power cords. Lick sellotape curl into a furry donut immediately regret falling into bathtub or you call this cat food? and fall asleep on the washing machine. Purr for no reason hack up furballs and pelt around the house and up and down stairs chasing phantoms. If it smells like fish eat as much as you wish. Unwrap toilet paper chew iPad power cord white cat sleeps on a black shirt lick the other cats. Lounge in doorway mew. Has closed eyes but still sees you sleep on keyboard, so hunt anything that moves lick sellotape but slap owner's face at 5am until human fills food dish if it smells like fish eat as much as you wish. Meow to be let in find empty spot in cupboard and sleep all day and thug cat sit by the fire burrow under covers always hungry. Swat at dog hide when guests come over purrrrrr chew on cable so mark territory, yet howl on top of tall thing or find something else more interesting. Chase mice kitten is playing with dead mouse. Sit and stare if it fits, i sits. Mark territory damn that dog , but step on your keyboard while you're gaming and then turn in a circle put butt in owner's face human give me attention meow or eat and than sleep on your face. Friends are not food jump around on couch, meow constantly until given food, or walk on car leaving trail of paw prints on hood and windshield, and spread kitty litter all over house, going to catch the red dot today going to catch the red dot today. Jump off balcony, onto stranger's head.</p>
|
||||||
|
<p>Meow to be let out damn that dog howl uncontrollably for no reason caticus cuteicus for play riveting piece on synthesizer keyboard. Meow loudly just to annoy owners the dog smells bad for eat the fat cats food, yet ignore the squirrels, you'll never catch them anyway cat snacks spread kitty litter all over house or hopped up on catnip. Spit up on light gray carpet instead of adjacent linoleum throwup on your pillow, so cat is love, cat is life yet human is washing you why halp oh the horror flee scratch hiss bite. Chase mice. Swat turds around the house hide at bottom of staircase to trip human. Meowing non stop for food howl on top of tall thing. Shake treat bag pee in human's bed until he cleans the litter box missing until dinner time. Have secret plans climb a tree, wait for a fireman jump to fireman then scratch his face bleghbleghvomit my furball really tie the room together. Chase dog then run away purr shake treat bag spit up on light gray carpet instead of adjacent linoleum but dream about hunting birds. Hiss at vacuum cleaner milk the cow lay on arms while you're using the keyboard sleep in the bathroom sink. Stare at ceiling touch water with paw then recoil in horror or refuse to leave cardboard box. Paw at your fat belly plan steps for world domination for going to catch the red dot today going to catch the red dot today slap owner's face at 5am until human fills food dish scratch at the door then walk away for intrigued by the shower, but steal the warm chair right after you get up. Fall asleep on the washing machine destroy couch as revenge scream at teh bath so love to play with owner's hair tie. Howl uncontrollably for no reason rub whiskers on bare skin act innocent. Cats making all the muffins lick butt and make a weird face meow all night having their mate disturbing sleeping humans human give me attention meow intently stare at the same spot. Sleep on dog bed, force dog to sleep on floor spot something, big eyes, big eyes, crouch, shake butt, prepare to pounce for wake up human for food at 4am or pooping rainbow while flying in a toasted bread costume in space sleep on keyboard put toy mouse in food bowl run out of litter box at full speed . Jump off balcony, onto stranger's head lick butt and make a weird face but go into a room to decide you didn't want to be in there anyway so then cats take over the world, pee in human's bed until he cleans the litter box and if it fits, i sits caticus cuteicus. Eats owners hair then claws head lounge in doorway, and hide when guests come over chase ball of string eat owner's food play riveting piece on synthesizer keyboard. Purrr purr littel cat, little cat purr purr spit up on light gray carpet instead of adjacent linoleum kitty loves pigs yet damn that dog meow or walk on car leaving trail of paw prints on hood and windshield. Roll on the floor purring your whiskers off meow all night having their mate disturbing sleeping humans need to chase tail meow hide when guests come over. Soft kitty warm kitty little ball of furr destroy the blinds meow leave hair everywhere attack dog, run away and pretend to be victim. Going to catch the red dot today going to catch the red dot today instantly break out into full speed gallop across the house for no reason meow so hide when guests come over, yet hide at bottom of staircase to trip human toy mouse squeak roll over claws in your leg. Cat slap dog in face lick plastic bags why must they do that.</p>
|
||||||
|
<p>Jump launch to pounce upon little yarn mouse, bare fangs at toy run hide in litter box until treats are fed touch water with paw then recoil in horror then cats take over the world i could pee on this if i had the energy. Lie on your belly and purr when you are asleep toy mouse squeak roll over so stick butt in face you call this cat food? and behind the couch curl up and sleep on the freshly laundered towels yet love to play with owner's hair tie. Knock dish off table head butt cant eat out of my own dish walk on car leaving trail of paw prints on hood and windshield find something else more interesting cats go for world domination, spit up on light gray carpet instead of adjacent linoleum sit in box. Missing until dinner time put toy mouse in food bowl run out of litter box at full speed but poop in the plant pot and nap all day caticus cuteicus. Leave hair everywhere attack feet mrow bleghbleghvomit my furball really tie the room together meowwww eat grass, throw it back up. Hate dog meowzer! find something else more interesting, yet cat snacks, so scratch at the door then walk away chase mice. Chase laser scratch the box plan steps for world domination massacre a bird in the living room and then look like the cutest and most innocent animal on the planet for stare at ceiling light and who's the baby. Stare at ceiling Gate keepers of hell, for licks paws intently sniff hand. Pooping rainbow while flying in a toasted bread costume in space. Gnaw the corn cob. Lick yarn hanging out of own butt stare at ceiling lick butt and make a weird face eat and than sleep on your face. Meow all night having their mate disturbing sleeping humans attack feet, so poop on grasses stare at wall turn and meow stare at wall some more meow again continue staring yet purr. Have my breakfast spaghetti yarn. Cats secretly make all the worlds muffins throwup on your pillow plays league of legends. Lick the plastic bag scratch at the door then walk away. Unwrap toilet paper meow to be let in walk on car leaving trail of paw prints on hood and windshield yet hide from vacuum cleaner or massacre a bird in the living room and then look like the cutest and most innocent animal on the planet. Purr lick the curtain just to be annoying go into a room to decide you didn't want to be in there anyway attack feet, and spit up on light gray carpet instead of adjacent linoleum yet lick plastic bags. Spit up on light gray carpet instead of adjacent linoleum touch water with paw then recoil in horror so cat snacks. Purr. Lick sellotape please stop looking at your phone and pet me yet stick butt in face meow. Groom yourself 4 hours - checked, have your beauty sleep 18 hours - checked, be fabulous for the rest of the day - checked! tuxedo cats always looking dapper but purrrrrr. Claws in your leg i could pee on this if i had the energy. Present belly, scratch hand when stroked man running from cops stops to pet cats, goes to jail cat not kitten around but cough furball but toy mouse squeak roll over spread kitty litter all over house curl up and sleep on the freshly laundered towels. Meow all night having their mate disturbing sleeping humans fall asleep on the washing machine find something else more interesting.</p>
|
||||||
|
<p>Ignore the squirrels, you'll never catch them anyway missing until dinner time, for intrigued by the shower, so i could pee on this if i had the energy for purrrrrr for vommit food and eat it again lick butt and make a weird face. Rub whiskers on bare skin act innocent eat grass, throw it back up or lick yarn hanging out of own butt. I am the best cat is love, cat is life, or sleep nap, mew but meoooow!. Meowzer!. Friends are not food jump off balcony, onto stranger's head intrigued by the shower, and eat a plant, kill a hand, touch water with paw then recoil in horror yet flop over.</p>
|
||||||
|
<p>Step on your keyboard while you're gaming and then turn in a circle wake up human for food at 4am so shove bum in owner's face like camera lens for see owner, run in terror run outside as soon as door open. Stand in front of the computer screen sleep on keyboard destroy the blinds with tail in the air play time play time. Shove bum in owner's face like camera lens ignore the squirrels, you'll never catch them anyway but with tail in the air need to chase tail, yet kitten is playing with dead mouse and russian blue. Hopped up on catnip refuse to leave cardboard box you call this cat food? walk on car leaving trail of paw prints on hood and windshield. Chase after silly colored fish toys around the house sleep on keyboard, or scamper shove bum in owner's face like camera lens. Groom yourself 4 hours - checked, have your beauty sleep 18 hours - checked, be fabulous for the rest of the day - checked! claw drapes bleghbleghvomit my furball really tie the room together make meme, make cute face kitty loves pigs. Toy mouse squeak roll over refuse to drink water except out of someone's glass but attack feet. Sleep on keyboard. Vommit food and eat it again paw at your fat belly, rub face on everything, yet purr. Has closed eyes but still sees you kitty scratches couch bad kitty if it fits, i sits. Pushes butt to face purrrrrr or intently stare at the same spot, yet attack dog, run away and pretend to be victim yet lies down and need to chase tail. Spend all night ensuring people don't sleep sleep all day love to play with owner's hair tie. I could pee on this if i had the energy lick butt stare out the window. Make meme, make cute face. Chase after silly colored fish toys around the house. Leave fur on owners clothes poop in the plant pot. Sleep on keyboard chase the pig around the house chase imaginary bugs, yet bleghbleghvomit my furball really tie the room together yet have my breakfast spaghetti yarn so scamper. Need to chase tail meow for food, then when human fills food dish, take a few bites of food and continue meowing for pee in the shoe thinking longingly about tuna brine yet purrr purr littel cat, little cat purr purr lie on your belly and purr when you are asleep. Lounge in doorway poop on grasses for lounge in doorway for chew iPad power cord.</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Byline from '~/components/Byline'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
head: {
|
||||||
|
title: 'The Cat'
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Byline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- You should not write any CSS here, but use ~/assets/main.css instead -->
|
BIN
examples/with-amp/static/cat.jpg
Normal file
BIN
examples/with-amp/static/cat.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
BIN
examples/with-amp/static/dog.jpg
Normal file
BIN
examples/with-amp/static/dog.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 65 KiB |
@ -389,6 +389,7 @@ function fixPrepatch(to, ___) {
|
|||||||
|
|
||||||
instances.forEach((instance, i) => {
|
instances.forEach((instance, i) => {
|
||||||
if (!instance) return
|
if (!instance) return
|
||||||
|
if (to.matched[i].path.indexOf(':') === -1) return // If not a dyanmic route, skip
|
||||||
if (instance.constructor._dataRefresh && _lastPaths[i] === instance.constructor._path && typeof instance.constructor.options.data === 'function') {
|
if (instance.constructor._dataRefresh && _lastPaths[i] === instance.constructor._path && typeof instance.constructor.options.data === 'function') {
|
||||||
const newData = instance.constructor.options.data.call(instance)
|
const newData = instance.constructor.options.data.call(instance)
|
||||||
for (let key in newData) {
|
for (let key in newData) {
|
||||||
|
@ -126,6 +126,10 @@ async function createApp (ssrContext) {
|
|||||||
key = '$' + key
|
key = '$' + key
|
||||||
// Add into app
|
// Add into app
|
||||||
app[key] = value
|
app[key] = value
|
||||||
|
<% if (store) { %>
|
||||||
|
// Add into store
|
||||||
|
store[key] = app[key]
|
||||||
|
<% } %>
|
||||||
// Check if plugin not already installed
|
// Check if plugin not already installed
|
||||||
const installKey = '__nuxt_' + key + '_installed__'
|
const installKey = '__nuxt_' + key + '_installed__'
|
||||||
if (Vue[installKey]) return
|
if (Vue[installKey]) return
|
||||||
@ -140,10 +144,6 @@ async function createApp (ssrContext) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
<% if (store) { %>
|
|
||||||
// Add into store
|
|
||||||
store[key] = app[key]
|
|
||||||
<% } %>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<% if (store) { %>
|
<% if (store) { %>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<% if (middleware) { %>
|
<% if (middleware) { %>
|
||||||
let files = require.context('@/middleware', false, /^\.\/(?!<%= ignorePrefix %>).*\.(js|ts)$/)
|
let files = require.context('@/middleware', false, /^\.\/(?!<%= ignorePrefix %>).*\.(<%= extensions %>)$/)
|
||||||
let filenames = files.keys()
|
let filenames = files.keys()
|
||||||
|
|
||||||
function getModule (filename) {
|
function getModule (filename) {
|
||||||
@ -12,7 +12,7 @@ let middleware = {}
|
|||||||
|
|
||||||
// Generate the middleware
|
// Generate the middleware
|
||||||
for (let filename of filenames) {
|
for (let filename of filenames) {
|
||||||
let name = filename.replace(/^\.\//, '').replace(/\.(js|ts)$/, '')
|
let name = filename.replace(/^\.\//, '').replace(/\.(<%= extensions %>)$/, '')
|
||||||
middleware[name] = getModule(filename)
|
middleware[name] = getModule(filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import Vuex from 'vuex'
|
|||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
// Recursive find files in {srcDir}/store
|
// Recursive find files in {srcDir}/store
|
||||||
const files = require.context('@/store', true, /^\.\/(?!<%= ignorePrefix %>).*\.(js|ts)$/)
|
const files = require.context('@/store', true, /^\.\/(?!<%= ignorePrefix %>).*\.(<%= extensions %>)$/)
|
||||||
const filenames = files.keys()
|
const filenames = files.keys()
|
||||||
|
|
||||||
// Store
|
// Store
|
||||||
@ -30,7 +30,7 @@ if (typeof storeData !== 'function') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let filename of filenames) {
|
for (let filename of filenames) {
|
||||||
let name = filename.replace(/^\.\//, '').replace(/\.(js|ts)$/, '')
|
let name = filename.replace(/^\.\//, '').replace(/\.(<%= extensions %>)$/, '')
|
||||||
if (name === 'index') continue
|
if (name === 'index') continue
|
||||||
|
|
||||||
let namePath = name.split(/\//)
|
let namePath = name.split(/\//)
|
||||||
|
@ -127,11 +127,16 @@ export async function setContext(app, context) {
|
|||||||
if (!status) return
|
if (!status) return
|
||||||
app.context._redirected = true // Used in middleware
|
app.context._redirected = true // Used in middleware
|
||||||
// if only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' })
|
// if only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' })
|
||||||
if (typeof status === 'string' && (typeof path === 'undefined' || typeof path === 'object')) {
|
let pathType = typeof path
|
||||||
|
if (typeof status !== 'number' && (pathType === 'undefined' || pathType === 'object')) {
|
||||||
query = path || {}
|
query = path || {}
|
||||||
path = status
|
path = status
|
||||||
|
pathType = typeof path
|
||||||
status = 302
|
status = 302
|
||||||
}
|
}
|
||||||
|
if (pathType === 'object') {
|
||||||
|
path = app.router.resolve(path).href
|
||||||
|
}
|
||||||
// "/absolute/route", "./relative/route" or "../relative/route"
|
// "/absolute/route", "./relative/route" or "../relative/route"
|
||||||
if (/(^[.]{1,2}\/)|(^\/(?!\/))/.test(path)) {
|
if (/(^[.]{1,2}\/)|(^\/(?!\/))/.test(path)) {
|
||||||
app.context.next({
|
app.context.next({
|
||||||
|
@ -9,15 +9,14 @@ const { join, resolve, basename, extname, dirname } = require('path')
|
|||||||
const MFS = require('memory-fs')
|
const MFS = require('memory-fs')
|
||||||
const webpackDevMiddleware = require('webpack-dev-middleware')
|
const webpackDevMiddleware = require('webpack-dev-middleware')
|
||||||
const webpackHotMiddleware = require('webpack-hot-middleware')
|
const webpackHotMiddleware = require('webpack-hot-middleware')
|
||||||
const { r, wp, wChunk, createRoutes, sequence, relativeTo, isPureObject, waitFor, rmCache } = require('../common/utils')
|
|
||||||
const Debug = require('debug')
|
const Debug = require('debug')
|
||||||
const Glob = require('glob')
|
const Glob = require('glob')
|
||||||
|
const { r, wp, wChunk, createRoutes, sequence, relativeTo, waitFor } = require('../common/utils')
|
||||||
|
const { Options } = require('../common')
|
||||||
const clientWebpackConfig = require('./webpack/client.config.js')
|
const clientWebpackConfig = require('./webpack/client.config.js')
|
||||||
const serverWebpackConfig = require('./webpack/server.config.js')
|
const serverWebpackConfig = require('./webpack/server.config.js')
|
||||||
const dllWebpackConfig = require('./webpack/dll.config.js')
|
const dllWebpackConfig = require('./webpack/dll.config.js')
|
||||||
const vueLoaderConfig = require('./webpack/vue-loader.config')
|
const upath = require('upath')
|
||||||
const styleLoader = require('./webpack/style-loader')
|
|
||||||
const { Options } = require('../common')
|
|
||||||
|
|
||||||
const debug = Debug('nuxt:build')
|
const debug = Debug('nuxt:build')
|
||||||
debug.color = 2 // Force green color
|
debug.color = 2 // Force green color
|
||||||
@ -44,10 +43,6 @@ module.exports = class Builder {
|
|||||||
// Helper to resolve build paths
|
// Helper to resolve build paths
|
||||||
this.relativeToBuild = (...args) => relativeTo(this.options.buildDir, ...args)
|
this.relativeToBuild = (...args) => relativeTo(this.options.buildDir, ...args)
|
||||||
|
|
||||||
// Bind styleLoader and vueLoader
|
|
||||||
this.styleLoader = styleLoader.bind(this)
|
|
||||||
this.vueLoader = vueLoaderConfig.bind(this)
|
|
||||||
|
|
||||||
this._buildStatus = STATUS.INITIAL
|
this._buildStatus = STATUS.INITIAL
|
||||||
|
|
||||||
// Stop watching on nuxt.close()
|
// Stop watching on nuxt.close()
|
||||||
@ -118,20 +113,6 @@ module.exports = class Builder {
|
|||||||
// Call before hook
|
// Call before hook
|
||||||
await this.nuxt.callHook('build:before', this, this.options.build)
|
await this.nuxt.callHook('build:before', this, this.options.build)
|
||||||
|
|
||||||
// Map postcss plugins into instances on object mode once
|
|
||||||
if (isPureObject(this.options.build.postcss)) {
|
|
||||||
if (isPureObject(this.options.build.postcss.plugins)) {
|
|
||||||
this.options.build.postcss.plugins = Object.keys(this.options.build.postcss.plugins)
|
|
||||||
.map(p => {
|
|
||||||
const plugin = require(this.nuxt.resolvePath(p))
|
|
||||||
const opts = this.options.build.postcss.plugins[p]
|
|
||||||
if (opts === false) return // Disabled
|
|
||||||
const instance = plugin(opts)
|
|
||||||
return instance
|
|
||||||
}).filter(e => e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if pages dir exists and warn if not
|
// Check if pages dir exists and warn if not
|
||||||
this._nuxtPages = typeof this.options.build.createRoutes !== 'function'
|
this._nuxtPages = typeof this.options.build.createRoutes !== 'function'
|
||||||
if (this._nuxtPages) {
|
if (this._nuxtPages) {
|
||||||
@ -229,6 +210,7 @@ module.exports = class Builder {
|
|||||||
]
|
]
|
||||||
const templateVars = {
|
const templateVars = {
|
||||||
options: this.options,
|
options: this.options,
|
||||||
|
extensions: this.options.extensions.map((ext) => ext.replace(/^\./, '')).join('|'),
|
||||||
messages: this.options.messages,
|
messages: this.options.messages,
|
||||||
uniqBy: _.uniqBy,
|
uniqBy: _.uniqBy,
|
||||||
isDev: this.options.dev,
|
isDev: this.options.dev,
|
||||||
@ -485,12 +467,6 @@ module.exports = class Builder {
|
|||||||
compiler.watch(this.options.watchers.webpack, (err) => {
|
compiler.watch(this.options.watchers.webpack, (err) => {
|
||||||
/* istanbul ignore if */
|
/* istanbul ignore if */
|
||||||
if (err) return reject(err)
|
if (err) return reject(err)
|
||||||
// not keep modified or deleted items in Vue.prototype
|
|
||||||
Object.keys(require.cache).forEach(key => {
|
|
||||||
if (!/(node_modules\/postcss)|(\.node$)/.test(key)) {
|
|
||||||
rmCache(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@ -544,7 +520,7 @@ module.exports = class Builder {
|
|||||||
|
|
||||||
watchFiles() {
|
watchFiles() {
|
||||||
const src = this.options.srcDir
|
const src = this.options.srcDir
|
||||||
const patterns = [
|
let patterns = [
|
||||||
r(src, 'layouts'),
|
r(src, 'layouts'),
|
||||||
r(src, 'store'),
|
r(src, 'store'),
|
||||||
r(src, 'middleware'),
|
r(src, 'middleware'),
|
||||||
@ -558,6 +534,8 @@ module.exports = class Builder {
|
|||||||
r(src, 'pages/**/*.{vue,js}')
|
r(src, 'pages/**/*.{vue,js}')
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
patterns = _.map(patterns, p => upath.normalizeSafe(p))
|
||||||
|
|
||||||
const options = Object.assign({}, this.options.watchers.chokidar, {
|
const options = Object.assign({}, this.options.watchers.chokidar, {
|
||||||
ignoreInitial: true
|
ignoreInitial: true
|
||||||
})
|
})
|
||||||
@ -570,7 +548,8 @@ module.exports = class Builder {
|
|||||||
.on('unlink', refreshFiles)
|
.on('unlink', refreshFiles)
|
||||||
|
|
||||||
// Watch for custom provided files
|
// Watch for custom provided files
|
||||||
this.customFilesWatcher = chokidar.watch(_.uniq(this.options.build.watch), options)
|
const watchFiles = _.map(_.uniq(this.options.build.watch), p => upath.normalizeSafe(p))
|
||||||
|
this.customFilesWatcher = chokidar.watch(watchFiles, options)
|
||||||
.on('change', refreshFiles)
|
.on('change', refreshFiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||||
const { cloneDeep } = require('lodash')
|
const { cloneDeep } = require('lodash')
|
||||||
const { join, resolve } = require('path')
|
const { join, resolve } = require('path')
|
||||||
const webpack = require('webpack')
|
|
||||||
const { isUrl, urlJoin } = require('../../common/utils')
|
const { isUrl, urlJoin } = require('../../common/utils')
|
||||||
|
const vueLoader = require('./vue-loader')
|
||||||
|
const styleLoader = require('./style-loader')
|
||||||
const TimeFixPlugin = require('./plugins/timefix')
|
const TimeFixPlugin = require('./plugins/timefix')
|
||||||
const WarnFixPlugin = require('./plugins/warnfix')
|
const WarnFixPlugin = require('./plugins/warnfix')
|
||||||
|
|
||||||
@ -17,7 +18,6 @@ const WarnFixPlugin = require('./plugins/warnfix')
|
|||||||
module.exports = function webpackBaseConfig({ name, isServer }) {
|
module.exports = function webpackBaseConfig({ name, isServer }) {
|
||||||
const config = {
|
const config = {
|
||||||
name,
|
name,
|
||||||
devtool: this.options.dev ? 'cheap-module-eval-source-map' : false,
|
|
||||||
entry: {
|
entry: {
|
||||||
app: null
|
app: null
|
||||||
},
|
},
|
||||||
@ -35,7 +35,7 @@ module.exports = function webpackBaseConfig({ name, isServer }) {
|
|||||||
hints: this.options.dev ? false : 'warning'
|
hints: this.options.dev ? false : 'warning'
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.js', '.json', '.vue', '.ts'],
|
extensions: ['.js', '.json', '.vue'],
|
||||||
alias: {
|
alias: {
|
||||||
'~': join(this.options.srcDir),
|
'~': join(this.options.srcDir),
|
||||||
'~~': join(this.options.rootDir),
|
'~~': join(this.options.rootDir),
|
||||||
@ -57,7 +57,7 @@ module.exports = function webpackBaseConfig({ name, isServer }) {
|
|||||||
{
|
{
|
||||||
test: /\.vue$/,
|
test: /\.vue$/,
|
||||||
loader: 'vue-loader',
|
loader: 'vue-loader',
|
||||||
options: this.vueLoader({ isServer })
|
options: vueLoader.call(this, { isServer })
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.js$/,
|
test: /\.js$/,
|
||||||
@ -65,11 +65,11 @@ module.exports = function webpackBaseConfig({ name, isServer }) {
|
|||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
options: this.getBabelOptions({ isServer })
|
options: this.getBabelOptions({ isServer })
|
||||||
},
|
},
|
||||||
{ test: /\.css$/, use: this.styleLoader('css') },
|
{ test: /\.css$/, use: styleLoader.call(this, 'css') },
|
||||||
{ test: /\.less$/, use: this.styleLoader('less', 'less-loader') },
|
{ test: /\.less$/, use: styleLoader.call(this, 'less', 'less-loader') },
|
||||||
{ test: /\.sass$/, use: this.styleLoader('sass', {loader: 'sass-loader', options: { indentedSyntax: true }}) },
|
{ test: /\.sass$/, use: styleLoader.call(this, 'sass', {loader: 'sass-loader', options: { indentedSyntax: true }}) },
|
||||||
{ test: /\.scss$/, use: this.styleLoader('scss', 'sass-loader') },
|
{ test: /\.scss$/, use: styleLoader.call(this, 'scss', 'sass-loader') },
|
||||||
{ test: /\.styl(us)?$/, use: this.styleLoader('stylus', 'stylus-loader') },
|
{ test: /\.styl(us)?$/, use: styleLoader.call(this, 'stylus', 'stylus-loader') },
|
||||||
{
|
{
|
||||||
test: /\.(png|jpe?g|gif|svg)$/,
|
test: /\.(png|jpe?g|gif|svg)$/,
|
||||||
loader: 'url-loader',
|
loader: 'url-loader',
|
||||||
@ -116,25 +116,6 @@ module.exports = function webpackBaseConfig({ name, isServer }) {
|
|||||||
config.plugins.push(new ExtractTextPlugin(extractOptions))
|
config.plugins.push(new ExtractTextPlugin(extractOptions))
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------
|
|
||||||
// Dev specific config
|
|
||||||
// --------------------------------------
|
|
||||||
if (this.options.dev) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------
|
|
||||||
// Production specific config
|
|
||||||
// --------------------------------------
|
|
||||||
if (!this.options.dev) {
|
|
||||||
// This is needed in webpack 2 for minify CSS
|
|
||||||
config.plugins.push(
|
|
||||||
new webpack.LoaderOptionsPlugin({
|
|
||||||
minimize: true
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone deep avoid leaking config between Client and Server
|
// Clone deep avoid leaking config between Client and Server
|
||||||
return cloneDeep(config)
|
return cloneDeep(config)
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,10 @@ module.exports = function webpackClientConfig() {
|
|||||||
config.entry.app = resolve(this.options.buildDir, 'client.js')
|
config.entry.app = resolve(this.options.buildDir, 'client.js')
|
||||||
config.entry.vendor = this.vendor()
|
config.entry.vendor = this.vendor()
|
||||||
|
|
||||||
|
// Config devtool
|
||||||
|
config.devtool = this.options.dev ? 'cheap-source-map' : false
|
||||||
|
config.output.devtoolModuleFilenameTemplate = '[absolute-resource-path]'
|
||||||
|
|
||||||
// Add CommonChunks plugin
|
// Add CommonChunks plugin
|
||||||
commonChunksPlugin.call(this, config)
|
commonChunksPlugin.call(this, config)
|
||||||
|
|
||||||
|
73
lib/builder/webpack/postcss.js
Normal file
73
lib/builder/webpack/postcss.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
const { existsSync } = require('fs')
|
||||||
|
const { resolve } = require('path')
|
||||||
|
const { cloneDeep } = require('lodash')
|
||||||
|
const { isPureObject } = require('../../common/utils')
|
||||||
|
|
||||||
|
module.exports = function postcssConfig() {
|
||||||
|
let config = cloneDeep(this.options.build.postcss)
|
||||||
|
|
||||||
|
/* istanbul ignore if */
|
||||||
|
if (!config) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for postCSS config file and use it if exists
|
||||||
|
// https://github.com/michael-ciniawsky/postcss-load-config
|
||||||
|
for (let dir of [this.options.srcDir, this.options.rootDir]) {
|
||||||
|
for (let file of ['postcss.config.js', '.postcssrc.js', '.postcssrc', '.postcssrc.json', '.postcssrc.yaml']) {
|
||||||
|
if (existsSync(resolve(dir, file))) {
|
||||||
|
const postcssConfigPath = resolve(dir, file)
|
||||||
|
return {
|
||||||
|
sourceMap: this.options.build.cssSourceMap,
|
||||||
|
config: {
|
||||||
|
path: postcssConfigPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize
|
||||||
|
if (Array.isArray(config)) {
|
||||||
|
config = { plugins: config }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply default plugins
|
||||||
|
if (isPureObject(config)) {
|
||||||
|
config = Object.assign({
|
||||||
|
useConfigFile: false,
|
||||||
|
sourceMap: this.options.build.cssSourceMap,
|
||||||
|
plugins: {
|
||||||
|
// https://github.com/postcss/postcss-import
|
||||||
|
'postcss-import': {
|
||||||
|
root: this.options.rootDir,
|
||||||
|
path: [
|
||||||
|
this.options.srcDir,
|
||||||
|
this.options.rootDir,
|
||||||
|
...this.options.modulesDir
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// https://github.com/postcss/postcss-url
|
||||||
|
'postcss-url': {},
|
||||||
|
|
||||||
|
// http://cssnext.io/postcss
|
||||||
|
'postcss-cssnext': {}
|
||||||
|
}
|
||||||
|
}, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map postcss plugins into instances on object mode once
|
||||||
|
if (isPureObject(config) && isPureObject(config.plugins)) {
|
||||||
|
config.plugins = Object.keys(config.plugins)
|
||||||
|
.map(p => {
|
||||||
|
const plugin = require(this.nuxt.resolvePath(p))
|
||||||
|
const opts = config.plugins[p]
|
||||||
|
if (opts === false) return // Disabled
|
||||||
|
const instance = plugin(opts)
|
||||||
|
return instance
|
||||||
|
}).filter(e => e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
@ -14,16 +14,18 @@ const base = require('./base.config.js')
|
|||||||
module.exports = function webpackServerConfig() {
|
module.exports = function webpackServerConfig() {
|
||||||
let config = base.call(this, { name: 'server', isServer: true })
|
let config = base.call(this, { name: 'server', isServer: true })
|
||||||
|
|
||||||
// env object defined in nuxt.config.js
|
// Env object defined in nuxt.config.js
|
||||||
let env = {}
|
let env = {}
|
||||||
each(this.options.env, (value, key) => {
|
each(this.options.env, (value, key) => {
|
||||||
env['process.env.' + key] = (['boolean', 'number'].indexOf(typeof value) !== -1 ? value : JSON.stringify(value))
|
env['process.env.' + key] = (['boolean', 'number'].indexOf(typeof value) !== -1 ? value : JSON.stringify(value))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Config devtool
|
||||||
|
config.devtool = this.options.dev ? 'cheap-source-map' : false
|
||||||
|
|
||||||
config = Object.assign(config, {
|
config = Object.assign(config, {
|
||||||
target: 'node',
|
target: 'node',
|
||||||
node: false,
|
node: false,
|
||||||
devtool: 'source-map',
|
|
||||||
entry: resolve(this.options.buildDir, 'server.js'),
|
entry: resolve(this.options.buildDir, 'server.js'),
|
||||||
output: Object.assign({}, config.output, {
|
output: Object.assign({}, config.output, {
|
||||||
filename: 'server-bundle.js',
|
filename: 'server-bundle.js',
|
||||||
@ -62,13 +64,6 @@ module.exports = function webpackServerConfig() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// --------------------------------------
|
|
||||||
// Production specific config
|
|
||||||
// --------------------------------------
|
|
||||||
if (!this.options.dev) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extend config
|
// Extend config
|
||||||
if (typeof this.options.build.extend === 'function') {
|
if (typeof this.options.build.extend === 'function') {
|
||||||
const isDev = this.options.dev
|
const isDev = this.options.dev
|
||||||
|
@ -1,75 +1,81 @@
|
|||||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||||
const { join } = require('path')
|
const { join } = require('path')
|
||||||
|
const postcssConfig = require('./postcss')
|
||||||
|
|
||||||
module.exports = function styleLoader(ext, loaders = [], isVueLoader = false) {
|
module.exports = function styleLoader(ext, loaders = [], isVueLoader = false) {
|
||||||
// Normalize loaders
|
const sourceMap = Boolean(this.options.build.cssSourceMap)
|
||||||
loaders = (Array.isArray(loaders) ? loaders : [loaders]).map(loader => {
|
|
||||||
if (typeof loader === 'string') {
|
|
||||||
loader = { loader }
|
|
||||||
}
|
|
||||||
return Object.assign({
|
|
||||||
options: {
|
|
||||||
sourceMap: this.options.build.cssSourceMap
|
|
||||||
}
|
|
||||||
}, loader)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
// Normalize loaders
|
||||||
|
loaders = (Array.isArray(loaders) ? loaders : [loaders])
|
||||||
|
.map(loader => Object.assign({ options: { sourceMap } }, typeof loader === 'string' ? { loader } : loader))
|
||||||
|
|
||||||
|
// Prepare vue-style-loader
|
||||||
|
// https://github.com/vuejs/vue-style-loader
|
||||||
|
const vueStyleLoader = {
|
||||||
|
loader: 'vue-style-loader',
|
||||||
|
options: { sourceMap }
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Configure additional loaders --
|
||||||
|
|
||||||
|
// style-resources-loader
|
||||||
|
// https://github.com/yenshih/style-resources-loader
|
||||||
|
if (this.options.build.styleResources) {
|
||||||
|
loaders.push({
|
||||||
|
loader: 'style-resources-loader',
|
||||||
|
options: Object.assign({ sourceMap }, this.options.build.styleResources)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// postcss-loader
|
||||||
|
// vue-loader already provides it's own
|
||||||
// https://github.com/postcss/postcss-loader
|
// https://github.com/postcss/postcss-loader
|
||||||
let postcssLoader
|
if (!isVueLoader) {
|
||||||
if (!isVueLoader && this.options.build.postcss) {
|
const _postcssConfig = postcssConfig.call(this)
|
||||||
postcssLoader = {
|
if (_postcssConfig) {
|
||||||
loader: 'postcss-loader',
|
loaders.unshift({
|
||||||
options: this.options.build.postcss
|
loader: 'postcss-loader',
|
||||||
|
options: Object.assign({ sourceMap }, _postcssConfig)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// css-loader
|
||||||
// https://github.com/webpack-contrib/css-loader
|
// https://github.com/webpack-contrib/css-loader
|
||||||
const cssLoader = {
|
loaders.unshift({
|
||||||
loader: 'css-loader',
|
loader: 'css-loader',
|
||||||
options: {
|
options: {
|
||||||
minimize: true,
|
sourceMap,
|
||||||
importLoaders: 1,
|
minimize: !this.options.dev,
|
||||||
sourceMap: this.options.build.cssSourceMap,
|
importLoaders: loaders.length, // Important!
|
||||||
alias: {
|
alias: {
|
||||||
'/static': join(this.options.srcDir, 'static'),
|
'/static': join(this.options.srcDir, 'static'),
|
||||||
'/assets': join(this.options.srcDir, 'assets')
|
'/assets': join(this.options.srcDir, 'assets')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
// https://github.com/vuejs/vue-style-loader
|
|
||||||
const vueStyleLoader = {
|
|
||||||
loader: 'vue-style-loader',
|
|
||||||
options: {
|
|
||||||
sourceMap: this.options.build.cssSourceMap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// -- With extractCSS --
|
||||||
|
// TODO: Temporary disabled in dev mode for fixing source maps
|
||||||
|
// (We need `source-map` devtool for *.css modules)
|
||||||
if (this.options.build.extractCSS && !this.options.dev) {
|
if (this.options.build.extractCSS && !this.options.dev) {
|
||||||
return ExtractTextPlugin.extract({
|
// ExtractTextPlugin
|
||||||
fallback: vueStyleLoader,
|
// https://github.com/webpack-contrib/extract-text-webpack-plugin
|
||||||
use: [
|
const extractLoader = ExtractTextPlugin.extract({
|
||||||
cssLoader,
|
use: loaders,
|
||||||
postcssLoader,
|
fallback: vueStyleLoader
|
||||||
...loaders
|
|
||||||
].filter(l => l)
|
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/yenshih/style-resources-loader
|
// css-hot-loader
|
||||||
let styleResourcesLoader
|
// https://github.com/shepherdwind/css-hot-loader
|
||||||
if (this.options.build.styleResources) {
|
const hotLoader = {
|
||||||
styleResourcesLoader = {
|
loader: 'css-hot-loader',
|
||||||
loader: 'style-resources-loader',
|
options: { sourceMap }
|
||||||
options: this.options.build.styleResources
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.options.dev ? [ hotLoader ].concat(extractLoader) : extractLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
// -- Without extractCSS --
|
||||||
vueStyleLoader,
|
return [ vueStyleLoader ].concat(loaders)
|
||||||
cssLoader,
|
|
||||||
postcssLoader,
|
|
||||||
...loaders,
|
|
||||||
styleResourcesLoader
|
|
||||||
].filter(l => l)
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
const postcssConfig = require('./postcss')
|
||||||
|
const styleLoader = require('./style-loader')
|
||||||
|
|
||||||
module.exports = function vueLoader({ isServer }) {
|
module.exports = function vueLoader({ isServer }) {
|
||||||
// https://vue-loader.vuejs.org/en
|
// https://vue-loader.vuejs.org/en
|
||||||
const config = {
|
const config = {
|
||||||
postcss: this.options.build.postcss,
|
postcss: postcssConfig.call(this),
|
||||||
extractCSS: !!this.options.build.extractCSS,
|
extractCSS: !!this.options.build.extractCSS,
|
||||||
cssSourceMap: this.options.build.cssSourceMap,
|
cssSourceMap: this.options.build.cssSourceMap,
|
||||||
preserveWhitespace: false,
|
preserveWhitespace: false,
|
||||||
@ -11,12 +14,12 @@ module.exports = function vueLoader({ isServer }) {
|
|||||||
options: this.getBabelOptions({ isServer })
|
options: this.getBabelOptions({ isServer })
|
||||||
},
|
},
|
||||||
// Note: do not nest the `postcss` option under `loaders`
|
// Note: do not nest the `postcss` option under `loaders`
|
||||||
'css': this.styleLoader('css', [], true),
|
'css': styleLoader.call(this, 'css', [], true),
|
||||||
'less': this.styleLoader('less', 'less-loader', true),
|
'less': styleLoader.call(this, 'less', 'less-loader', true),
|
||||||
'scss': this.styleLoader('scss', 'sass-loader', true),
|
'scss': styleLoader.call(this, 'scss', 'sass-loader', true),
|
||||||
'sass': this.styleLoader('sass', {loader: 'sass-loader', options: { indentedSyntax: true }}, true),
|
'sass': styleLoader.call(this, 'sass', {loader: 'sass-loader', options: { indentedSyntax: true }}, true),
|
||||||
'stylus': this.styleLoader('stylus', 'stylus-loader', true),
|
'stylus': styleLoader.call(this, 'stylus', 'stylus-loader', true),
|
||||||
'styl': this.styleLoader('stylus', 'stylus-loader', true)
|
'styl': styleLoader.call(this, 'stylus', 'stylus-loader', true)
|
||||||
},
|
},
|
||||||
template: {
|
template: {
|
||||||
doctype: 'html' // For pug, see https://github.com/vuejs/vue-loader/issues/55
|
doctype: 'html' // For pug, see https://github.com/vuejs/vue-loader/issues/55
|
@ -1,21 +1,7 @@
|
|||||||
const PrettyError = require('pretty-error')
|
const PrettyError = require('pretty-error')
|
||||||
|
|
||||||
// Start default instance
|
// Start default instance
|
||||||
const pe = PrettyError.start()
|
const pe = new PrettyError()
|
||||||
|
|
||||||
// Configure prettyError instance
|
|
||||||
pe.skipPackage('regenerator-runtime')
|
|
||||||
pe.skipPackage('babel-runtime')
|
|
||||||
pe.skipPackage('core-js')
|
|
||||||
|
|
||||||
// Skip node internals
|
|
||||||
pe.skip((traceLine, lineNumber) => {
|
|
||||||
if (!traceLine.file) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
pe.skipNodeFiles()
|
|
||||||
|
|
||||||
// Console error unhandled promises
|
// Console error unhandled promises
|
||||||
process.on('unhandledRejection', function (err) {
|
process.on('unhandledRejection', function (err) {
|
||||||
|
@ -31,6 +31,9 @@ Options.from = function (_options) {
|
|||||||
if (typeof options.layoutTransition === 'string') {
|
if (typeof options.layoutTransition === 'string') {
|
||||||
options.layoutTransition = { name: options.layoutTransition }
|
options.layoutTransition = { name: options.layoutTransition }
|
||||||
}
|
}
|
||||||
|
if (typeof options.extensions === 'string') {
|
||||||
|
options.extensions = [ options.extensions ]
|
||||||
|
}
|
||||||
|
|
||||||
const hasValue = v => typeof v === 'string' && v
|
const hasValue = v => typeof v === 'string' && v
|
||||||
options.rootDir = hasValue(options.rootDir) ? options.rootDir : process.cwd()
|
options.rootDir = hasValue(options.rootDir) ? options.rootDir : process.cwd()
|
||||||
@ -53,10 +56,16 @@ Options.from = function (_options) {
|
|||||||
|
|
||||||
// Populate modulesDir
|
// Populate modulesDir
|
||||||
options.modulesDir = []
|
options.modulesDir = []
|
||||||
.concat(options.modulesDir, join(options.nuxtDir, 'node_modules'))
|
.concat(join(options.nuxtDir, 'node_modules'))
|
||||||
|
.concat(options.modulesDir)
|
||||||
.filter(dir => hasValue(dir))
|
.filter(dir => hasValue(dir))
|
||||||
.map(dir => resolve(options.rootDir, dir))
|
.map(dir => resolve(options.rootDir, dir))
|
||||||
|
|
||||||
|
// Sanitize extensions
|
||||||
|
if (options.extensions.indexOf('js') === -1) {
|
||||||
|
options.extensions.unshift('js')
|
||||||
|
}
|
||||||
|
|
||||||
// If app.html is defined, set the template path to the user template
|
// If app.html is defined, set the template path to the user template
|
||||||
options.appTemplatePath = resolve(options.buildDir, 'views/app.template.html')
|
options.appTemplatePath = resolve(options.buildDir, 'views/app.template.html')
|
||||||
if (existsSync(join(options.srcDir, 'app.html'))) {
|
if (existsSync(join(options.srcDir, 'app.html'))) {
|
||||||
@ -91,69 +100,6 @@ Options.from = function (_options) {
|
|||||||
options.build.cssSourceMap = options.dev
|
options.build.cssSourceMap = options.dev
|
||||||
}
|
}
|
||||||
|
|
||||||
// Postcss
|
|
||||||
// 1. Check if it is explicitly disabled by false value
|
|
||||||
// ... Disable all postcss loaders
|
|
||||||
// 2. Check if any standard source of postcss config exists
|
|
||||||
// ... Make postcss = { config: { path } }
|
|
||||||
// 3. Else (Easy Usage)
|
|
||||||
// ... Auto merge it with defaults
|
|
||||||
if (options.build.postcss !== false) {
|
|
||||||
// Detect postcss config existence
|
|
||||||
// https://github.com/michael-ciniawsky/postcss-load-config
|
|
||||||
let postcssConfigPath
|
|
||||||
for (let dir of [options.srcDir, options.rootDir]) {
|
|
||||||
for (let file of ['postcss.config.js', '.postcssrc.js', '.postcssrc', '.postcssrc.json', '.postcssrc.yaml']) {
|
|
||||||
if (existsSync(resolve(dir, file))) {
|
|
||||||
postcssConfigPath = resolve(dir, file)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (postcssConfigPath) break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default postcss options
|
|
||||||
if (postcssConfigPath) {
|
|
||||||
debug(`Using PostCSS config file: ${postcssConfigPath}`)
|
|
||||||
options.build.postcss = {
|
|
||||||
sourceMap: options.build.cssSourceMap,
|
|
||||||
// https://github.com/postcss/postcss-loader/blob/master/lib/index.js#L79
|
|
||||||
config: {
|
|
||||||
path: postcssConfigPath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (Object.keys(options.build.postcss).length) {
|
|
||||||
debug('Using PostCSS config from `build.postcss`')
|
|
||||||
}
|
|
||||||
// Normalize & Apply default plugins
|
|
||||||
if (Array.isArray(options.build.postcss)) {
|
|
||||||
options.build.postcss = { plugins: options.build.postcss }
|
|
||||||
}
|
|
||||||
if (isPureObject(options.build.postcss)) {
|
|
||||||
options.build.postcss = Object.assign({
|
|
||||||
useConfigFile: false,
|
|
||||||
sourceMap: options.build.cssSourceMap,
|
|
||||||
plugins: {
|
|
||||||
// https://github.com/postcss/postcss-import
|
|
||||||
'postcss-import': {
|
|
||||||
root: options.rootDir,
|
|
||||||
path: [
|
|
||||||
options.srcDir,
|
|
||||||
options.rootDir,
|
|
||||||
...options.modulesDir
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// https://github.com/postcss/postcss-url
|
|
||||||
'postcss-url': {},
|
|
||||||
// http://cssnext.io/postcss
|
|
||||||
'postcss-cssnext': {}
|
|
||||||
}
|
|
||||||
}, options.build.postcss)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug errors
|
// Debug errors
|
||||||
if (options.debug === undefined) {
|
if (options.debug === undefined) {
|
||||||
options.debug = options.dev
|
options.debug = options.dev
|
||||||
@ -206,6 +152,7 @@ Options.defaults = {
|
|||||||
nuxtAppDir: resolve(__dirname, '../app'),
|
nuxtAppDir: resolve(__dirname, '../app'),
|
||||||
modulesDir: ['node_modules'], // ~> relative to options.rootDir
|
modulesDir: ['node_modules'], // ~> relative to options.rootDir
|
||||||
ignorePrefix: '-',
|
ignorePrefix: '-',
|
||||||
|
extensions: [],
|
||||||
build: {
|
build: {
|
||||||
analyze: false,
|
analyze: false,
|
||||||
profile: process.argv.includes('--profile'),
|
profile: process.argv.includes('--profile'),
|
||||||
@ -217,7 +164,7 @@ Options.defaults = {
|
|||||||
uglify: {},
|
uglify: {},
|
||||||
publicPath: '/_nuxt/',
|
publicPath: '/_nuxt/',
|
||||||
filenames: {
|
filenames: {
|
||||||
css: 'vendor.[contenthash].css',
|
css: '[name].[contenthash].css',
|
||||||
manifest: 'manifest.[hash].js',
|
manifest: 'manifest.[hash].js',
|
||||||
vendor: 'vendor.[chunkhash].js',
|
vendor: 'vendor.[chunkhash].js',
|
||||||
app: 'app.[chunkhash].js',
|
app: 'app.[chunkhash].js',
|
||||||
@ -321,14 +268,15 @@ Options.defaults = {
|
|||||||
resourceHints: true,
|
resourceHints: true,
|
||||||
ssr: undefined,
|
ssr: undefined,
|
||||||
http2: {
|
http2: {
|
||||||
push: false
|
push: false,
|
||||||
|
shouldPush: null
|
||||||
},
|
},
|
||||||
static: {},
|
static: {},
|
||||||
gzip: {
|
gzip: {
|
||||||
threshold: 0
|
threshold: 0
|
||||||
},
|
},
|
||||||
etag: {
|
etag: {
|
||||||
weak: true // Faster for responses > 5KB
|
weak: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watchers: {
|
watchers: {
|
||||||
|
@ -268,16 +268,3 @@ exports.createRoutes = function createRoutes(files, srcDir) {
|
|||||||
})
|
})
|
||||||
return cleanChildrenRoutes(routes)
|
return cleanChildrenRoutes(routes)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.rmCache = function rmCache(path) {
|
|
||||||
const mod = require.cache[path]
|
|
||||||
delete require.cache[path]
|
|
||||||
if (mod.parent && mod.parent.children) {
|
|
||||||
for (let i = 0; i < mod.parent.children.length; i++) {
|
|
||||||
if (mod.parent.children[i] === mod) {
|
|
||||||
mod.parent.children.splice(i, 1)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -74,6 +74,14 @@ module.exports = class MetaRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Emulate getPreloadFiles from vue-server-renderer (works for JS chunks only)
|
||||||
|
meta.getPreloadFiles = () => clientManifest.initial.map(r => ({
|
||||||
|
file: r,
|
||||||
|
fileWithoutQuery: r,
|
||||||
|
asType: 'script',
|
||||||
|
extension: 'js'
|
||||||
|
}))
|
||||||
|
|
||||||
// Set meta tags inside cache
|
// Set meta tags inside cache
|
||||||
this.cache.set(url, meta)
|
this.cache.set(url, meta)
|
||||||
|
|
||||||
|
93
lib/core/middleware/error.js
Normal file
93
lib/core/middleware/error.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
const Youch = require('@nuxtjs/youch')
|
||||||
|
const { join, resolve, relative, isAbsolute } = require('path')
|
||||||
|
const { readFile } = require('fs-extra')
|
||||||
|
|
||||||
|
module.exports = function errorMiddleware(err, req, res, next) {
|
||||||
|
// ensure statusCode, message and name fields
|
||||||
|
err.statusCode = err.statusCode || 500
|
||||||
|
err.message = err.message || 'Nuxt Server Error'
|
||||||
|
err.name = (!err.name || err.name === 'Error') ? 'NuxtServerError' : err.name
|
||||||
|
|
||||||
|
// We hide actual errors from end users, so show them on server logs
|
||||||
|
if (err.statusCode !== 404) {
|
||||||
|
console.error(err) // eslint-disable-line no-console
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendResponse = (content, type = 'text/html') => {
|
||||||
|
// Set Headers
|
||||||
|
res.statusCode = err.statusCode
|
||||||
|
res.statusMessage = err.name
|
||||||
|
res.setHeader('Content-Type', type + '; charset=utf-8')
|
||||||
|
res.setHeader('Content-Length', Buffer.byteLength(content))
|
||||||
|
|
||||||
|
// Send Response
|
||||||
|
res.end(content, 'utf-8')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if request accepts JSON
|
||||||
|
const hasReqHeader = (header, includes) => req.headers[header] && req.headers[header].toLowerCase().includes(includes)
|
||||||
|
const isJson = hasReqHeader('accept', 'application/json') || hasReqHeader('user-agent', 'curl/')
|
||||||
|
|
||||||
|
// Use basic errors when debug mode is disabled
|
||||||
|
if (!this.options.debug) {
|
||||||
|
// Json format is compatible with Youch json responses
|
||||||
|
const json = {
|
||||||
|
status: err.statusCode,
|
||||||
|
message: err.message,
|
||||||
|
name: err.name
|
||||||
|
}
|
||||||
|
if (isJson) {
|
||||||
|
sendResponse(JSON.stringify(json, undefined, 2), 'text/json')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const html = this.resources.errorTemplate(json)
|
||||||
|
sendResponse(html)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show stack trace
|
||||||
|
const youch = new Youch(err, req, readSource.bind(this))
|
||||||
|
if (isJson) {
|
||||||
|
youch.toJSON().then(json => { sendResponse(JSON.stringify(json, undefined, 2), 'text/json') })
|
||||||
|
} else {
|
||||||
|
youch.toHTML().then(html => { sendResponse(html) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readSource(frame) {
|
||||||
|
// Remove webpack:/// & query string from the end
|
||||||
|
const sanitizeName = name => name ? name.replace('webpack:///', '').split('?')[0] : null
|
||||||
|
frame.fileName = sanitizeName(frame.fileName)
|
||||||
|
|
||||||
|
// Return if fileName is unknown
|
||||||
|
if (!frame.fileName) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Possible paths for file
|
||||||
|
const searchPath = [
|
||||||
|
this.options.srcDir,
|
||||||
|
this.options.rootDir,
|
||||||
|
join(this.options.buildDir, 'dist'),
|
||||||
|
this.options.buildDir
|
||||||
|
]
|
||||||
|
|
||||||
|
// Scan filesystem for real source
|
||||||
|
for (let pathDir of searchPath) {
|
||||||
|
let fullPath = resolve(pathDir, frame.fileName)
|
||||||
|
let source = await readFile(fullPath, 'utf-8').catch(() => null)
|
||||||
|
if (source) {
|
||||||
|
frame.contents = source
|
||||||
|
frame.fullPath = fullPath
|
||||||
|
if (isAbsolute(frame.fileName)) {
|
||||||
|
frame.fileName = relative(this.options.rootDir, fullPath)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: use server bundle
|
||||||
|
if (!frame.contents) {
|
||||||
|
frame.contents = this.resources.serverBundle.files[frame.fileName]
|
||||||
|
}
|
||||||
|
}
|
77
lib/core/middleware/nuxt.js
Normal file
77
lib/core/middleware/nuxt.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
const generateETag = require('etag')
|
||||||
|
const fresh = require('fresh')
|
||||||
|
|
||||||
|
const { getContext } = require('../../common/utils')
|
||||||
|
|
||||||
|
module.exports = async function nuxtMiddleware(req, res, next) {
|
||||||
|
// Get context
|
||||||
|
const context = getContext(req, res)
|
||||||
|
|
||||||
|
res.statusCode = 200
|
||||||
|
try {
|
||||||
|
const result = await this.renderRoute(req.url, context)
|
||||||
|
await this.nuxt.callHook('render:route', req.url, result)
|
||||||
|
const { html, error, redirected, getPreloadFiles } = result
|
||||||
|
|
||||||
|
if (redirected) {
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
res.statusCode = context.nuxt.error.statusCode || 500
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add ETag header
|
||||||
|
if (!error && this.options.render.etag) {
|
||||||
|
const etag = generateETag(html, this.options.render.etag)
|
||||||
|
if (fresh(req.headers, { etag })) {
|
||||||
|
res.statusCode = 304
|
||||||
|
res.end()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res.setHeader('ETag', etag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP2 push headers for preload assets
|
||||||
|
if (!error && this.options.render.http2.push) {
|
||||||
|
// Parse resourceHints to extract HTTP.2 prefetch/push headers
|
||||||
|
// https://w3c.github.io/preload/#server-push-http-2
|
||||||
|
const pushAssets = []
|
||||||
|
const preloadFiles = getPreloadFiles()
|
||||||
|
const { shouldPush } = this.options.render.http2
|
||||||
|
const { publicPath } = this.resources.clientManifest
|
||||||
|
|
||||||
|
preloadFiles.forEach(({ file, asType, fileWithoutQuery, extension }) => {
|
||||||
|
// By default, we only preload scripts or css
|
||||||
|
if (!shouldPush && asType !== 'script' && asType !== 'style') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// User wants to explicitly control what to preload
|
||||||
|
if (shouldPush && !shouldPush(fileWithoutQuery, asType)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pushAssets.push(`<${publicPath}${file}>; rel=preload; as=${asType}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Pass with single Link header
|
||||||
|
// https://preloadFilesblog.cloudflare.com/http-2-server-push-with-multiple-assets-per-link-header
|
||||||
|
// https://www.w3.org/Protocols/9707-link-header.html
|
||||||
|
res.setHeader('Link', pushAssets.join(','))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send response
|
||||||
|
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
||||||
|
res.setHeader('Content-Length', Buffer.byteLength(html))
|
||||||
|
res.end(html, 'utf8')
|
||||||
|
return html
|
||||||
|
} catch (err) {
|
||||||
|
/* istanbul ignore if */
|
||||||
|
if (context && context.redirected) {
|
||||||
|
console.error(err) // eslint-disable-line no-console
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
next(err)
|
||||||
|
}
|
||||||
|
}
|
23
lib/core/middleware/open-in-editor.js
Normal file
23
lib/core/middleware/open-in-editor.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
const openInEditor = require('open-in-editor')
|
||||||
|
|
||||||
|
module.exports = function openInEditorMiddleware(req, res) {
|
||||||
|
// Lazy load open-in-editor
|
||||||
|
const editor = openInEditor.configure(this.options.editor)
|
||||||
|
|
||||||
|
// Parse Query
|
||||||
|
const query = req.url.split('?')[1].split('&').reduce((q, part) => {
|
||||||
|
const s = part.split('=')
|
||||||
|
q[s[0]] = decodeURIComponent(s[1])
|
||||||
|
return q
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('[open in editor]', query.file)
|
||||||
|
|
||||||
|
editor.open(query.file).then(() => {
|
||||||
|
res.end('opened in editor!')
|
||||||
|
}).catch(err => {
|
||||||
|
res.end(err)
|
||||||
|
})
|
||||||
|
}
|
@ -1,21 +1,23 @@
|
|||||||
const ansiHTML = require('ansi-html')
|
const ansiHTML = require('ansi-html')
|
||||||
const serialize = require('serialize-javascript')
|
const serialize = require('serialize-javascript')
|
||||||
const generateETag = require('etag')
|
|
||||||
const fresh = require('fresh')
|
|
||||||
const serveStatic = require('serve-static')
|
const serveStatic = require('serve-static')
|
||||||
const compression = require('compression')
|
const compression = require('compression')
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
const { join, resolve } = require('path')
|
const { join, resolve } = require('path')
|
||||||
const fs = require('fs-extra')
|
const fs = require('fs-extra')
|
||||||
const { createBundleRenderer } = require('vue-server-renderer')
|
const { createBundleRenderer } = require('vue-server-renderer')
|
||||||
const { getContext, setAnsiColors, isUrl, waitFor } = require('../common/utils')
|
const { setAnsiColors, isUrl, waitFor } = require('../common/utils')
|
||||||
const Debug = require('debug')
|
const Debug = require('debug')
|
||||||
const Youch = require('@nuxtjs/youch')
|
|
||||||
const { SourceMapConsumer } = require('source-map')
|
|
||||||
const connect = require('connect')
|
const connect = require('connect')
|
||||||
const { Options } = require('../common')
|
const { Options } = require('../common')
|
||||||
const MetaRenderer = require('./meta')
|
const MetaRenderer = require('./meta')
|
||||||
|
|
||||||
|
const errorMiddleware = require('./middleware/error')
|
||||||
|
const nuxtMiddleware = require('./middleware/nuxt')
|
||||||
|
const openInEditorMiddleware = require('./middleware/open-in-editor')
|
||||||
|
|
||||||
const debug = Debug('nuxt:render')
|
const debug = Debug('nuxt:render')
|
||||||
debug.color = 4 // Force blue color
|
debug.color = 4 // Force blue color
|
||||||
|
|
||||||
@ -223,28 +225,10 @@ module.exports = class Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// open in editor for debug mode only
|
// open in editor for debug mode only
|
||||||
const _this = this
|
|
||||||
if (this.options.debug && this.options.dev) {
|
if (this.options.debug && this.options.dev) {
|
||||||
this.useMiddleware({
|
this.useMiddleware({
|
||||||
path: '_open',
|
path: '__open-in-editor',
|
||||||
handler(req, res) {
|
handler: openInEditorMiddleware.bind(this)
|
||||||
// Lazy load open-in-editor
|
|
||||||
const openInEditor = require('open-in-editor')
|
|
||||||
const editor = openInEditor.configure(_this.options.editor)
|
|
||||||
// Parse Query
|
|
||||||
const query = req.url.split('?')[1].split('&').reduce((q, part) => {
|
|
||||||
const s = part.split('=')
|
|
||||||
q[s[0]] = decodeURIComponent(s[1])
|
|
||||||
return q
|
|
||||||
}, {})
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('[open in editor]', query.file)
|
|
||||||
editor.open(query.file).then(() => {
|
|
||||||
res.end('opened in editor!')
|
|
||||||
}).catch(err => {
|
|
||||||
res.end(err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,206 +254,12 @@ module.exports = class Renderer {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Finally use nuxtMiddleware
|
// Finally use nuxtMiddleware
|
||||||
this.useMiddleware(this.nuxtMiddleware.bind(this))
|
this.useMiddleware(nuxtMiddleware.bind(this))
|
||||||
|
|
||||||
// Error middleware for errors that occurred in middleware that declared above
|
// Error middleware for errors that occurred in middleware that declared above
|
||||||
// Middleware should exactly take 4 arguments
|
// Middleware should exactly take 4 arguments
|
||||||
// https://github.com/senchalabs/connect#error-middleware
|
// https://github.com/senchalabs/connect#error-middleware
|
||||||
this.useMiddleware(this.errorMiddleware.bind(this))
|
this.useMiddleware(errorMiddleware.bind(this))
|
||||||
}
|
|
||||||
|
|
||||||
async nuxtMiddleware(req, res, next) {
|
|
||||||
// Get context
|
|
||||||
const context = getContext(req, res)
|
|
||||||
|
|
||||||
res.statusCode = 200
|
|
||||||
try {
|
|
||||||
const result = await this.renderRoute(req.url, context)
|
|
||||||
await this.nuxt.callHook('render:route', req.url, result)
|
|
||||||
const { html, error, redirected, resourceHints } = result
|
|
||||||
|
|
||||||
if (redirected) {
|
|
||||||
return html
|
|
||||||
}
|
|
||||||
if (error) {
|
|
||||||
res.statusCode = context.nuxt.error.statusCode || 500
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add ETag header
|
|
||||||
if (!error && this.options.render.etag) {
|
|
||||||
const etag = generateETag(html, this.options.render.etag)
|
|
||||||
if (fresh(req.headers, { etag })) {
|
|
||||||
res.statusCode = 304
|
|
||||||
res.end()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
res.setHeader('ETag', etag)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP2 push headers
|
|
||||||
if (!error && this.options.render.http2.push) {
|
|
||||||
// Parse resourceHints to extract HTTP.2 prefetch/push headers
|
|
||||||
// https://w3c.github.io/preload/#server-push-http-2
|
|
||||||
const regex = /link rel="([^"]*)" href="([^"]*)" as="([^"]*)"/g
|
|
||||||
const pushAssets = []
|
|
||||||
let m
|
|
||||||
while (m = regex.exec(resourceHints)) { // eslint-disable-line no-cond-assign
|
|
||||||
const [, rel, href, as] = m
|
|
||||||
if (rel === 'preload') {
|
|
||||||
pushAssets.push(`<${href}>; rel=${rel}; as=${as}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Pass with single Link header
|
|
||||||
// https://blog.cloudflare.com/http-2-server-push-with-multiple-assets-per-link-header
|
|
||||||
res.setHeader('Link', pushAssets.join(','))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send response
|
|
||||||
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
|
||||||
res.setHeader('Content-Length', Buffer.byteLength(html))
|
|
||||||
res.end(html, 'utf8')
|
|
||||||
return html
|
|
||||||
} catch (err) {
|
|
||||||
/* istanbul ignore if */
|
|
||||||
if (context && context.redirected) {
|
|
||||||
console.error(err) // eslint-disable-line no-console
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
next(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
errorMiddleware(err, req, res, next) {
|
|
||||||
// ensure statusCode, message and name fields
|
|
||||||
err.statusCode = err.statusCode || 500
|
|
||||||
err.message = err.message || 'Nuxt Server Error'
|
|
||||||
err.name = (!err.name || err.name === 'Error') ? 'NuxtServerError' : err.name
|
|
||||||
|
|
||||||
// We hide actual errors from end users, so show them on server logs
|
|
||||||
if (err.statusCode !== 404) {
|
|
||||||
console.error(err) // eslint-disable-line no-console
|
|
||||||
}
|
|
||||||
|
|
||||||
const sendResponse = (content, type = 'text/html') => {
|
|
||||||
// Set Headers
|
|
||||||
res.statusCode = err.statusCode
|
|
||||||
res.statusMessage = err.name
|
|
||||||
res.setHeader('Content-Type', type + '; charset=utf-8')
|
|
||||||
res.setHeader('Content-Length', Buffer.byteLength(content))
|
|
||||||
|
|
||||||
// Send Response
|
|
||||||
res.end(content, 'utf-8')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if request accepts JSON
|
|
||||||
const hasReqHeader = (header, includes) => req.headers[header] && req.headers[header].toLowerCase().includes(includes)
|
|
||||||
const isJson = hasReqHeader('accept', 'application/json') || hasReqHeader('user-agent', 'curl/')
|
|
||||||
|
|
||||||
// Use basic errors when debug mode is disabled
|
|
||||||
if (!this.options.debug) {
|
|
||||||
// Json format is compatible with Youch json responses
|
|
||||||
const json = {
|
|
||||||
status: err.statusCode,
|
|
||||||
message: err.message,
|
|
||||||
name: err.name
|
|
||||||
}
|
|
||||||
if (isJson) {
|
|
||||||
sendResponse(JSON.stringify(json, undefined, 2), 'text/json')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const html = this.resources.errorTemplate(json)
|
|
||||||
sendResponse(html)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show stack trace
|
|
||||||
const youch = new Youch(err, req, this.readSource.bind(this))
|
|
||||||
if (isJson) {
|
|
||||||
youch.toJSON().then(json => { sendResponse(JSON.stringify(json, undefined, 2), 'text/json') })
|
|
||||||
} else {
|
|
||||||
youch.toHTML().then(html => { sendResponse(html) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async readSource(frame) {
|
|
||||||
const serverBundle = this.resources.serverBundle
|
|
||||||
|
|
||||||
// Remove webpack:/// & query string from the end
|
|
||||||
const sanitizeName = name => name ? name.replace('webpack:///', '').split('?')[0] : ''
|
|
||||||
|
|
||||||
// SourceMap Support for SSR Bundle
|
|
||||||
if (serverBundle && serverBundle.maps[frame.fileName]) {
|
|
||||||
// Initialize smc cache
|
|
||||||
if (!serverBundle.$maps) {
|
|
||||||
serverBundle.$maps = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read SourceMap object
|
|
||||||
const smc = serverBundle.$maps[frame.fileName] || new SourceMapConsumer(serverBundle.maps[frame.fileName])
|
|
||||||
serverBundle.$maps[frame.fileName] = smc
|
|
||||||
|
|
||||||
// Try to find original position
|
|
||||||
const { line, column, name, source } = smc.originalPositionFor({
|
|
||||||
line: frame.getLineNumber() || 0,
|
|
||||||
column: frame.getColumnNumber() || 0,
|
|
||||||
bias: SourceMapConsumer.LEAST_UPPER_BOUND
|
|
||||||
})
|
|
||||||
if (line) {
|
|
||||||
frame.lineNumber = line
|
|
||||||
}
|
|
||||||
/* istanbul ignore if */
|
|
||||||
if (column) {
|
|
||||||
frame.columnNumber = column
|
|
||||||
}
|
|
||||||
/* istanbul ignore if */
|
|
||||||
if (name) {
|
|
||||||
frame.functionName = name
|
|
||||||
}
|
|
||||||
if (source) {
|
|
||||||
frame.fileName = sanitizeName(source)
|
|
||||||
|
|
||||||
// Source detected, try to get original source code
|
|
||||||
const contents = smc.sourceContentFor(source)
|
|
||||||
if (contents) {
|
|
||||||
frame.contents = contents
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return if fileName is still unknown
|
|
||||||
if (!frame.fileName) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
frame.fileName = sanitizeName(frame.fileName)
|
|
||||||
|
|
||||||
// Try to read from SSR bundle files
|
|
||||||
if (serverBundle && serverBundle.files[frame.fileName]) {
|
|
||||||
frame.contents = serverBundle.files[frame.fileName]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Possible paths for file
|
|
||||||
const searchPath = [
|
|
||||||
this.options.rootDir,
|
|
||||||
join(this.options.buildDir, 'dist'),
|
|
||||||
this.options.srcDir,
|
|
||||||
this.options.buildDir
|
|
||||||
]
|
|
||||||
|
|
||||||
// Scan filesystem for real path
|
|
||||||
for (let pathDir of searchPath) {
|
|
||||||
let fullPath = resolve(pathDir, frame.fileName)
|
|
||||||
let source = await fs.readFile(fullPath, 'utf-8').catch(() => null)
|
|
||||||
if (source) {
|
|
||||||
if (!frame.contents) {
|
|
||||||
frame.contents = source
|
|
||||||
}
|
|
||||||
frame.fullPath = fullPath
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async renderRoute(url, context = {}) {
|
async renderRoute(url, context = {}) {
|
||||||
@ -490,7 +280,7 @@ module.exports = class Renderer {
|
|||||||
const ENV = this.options.env
|
const ENV = this.options.env
|
||||||
|
|
||||||
if (this.noSSR || spa) {
|
if (this.noSSR || spa) {
|
||||||
const { HTML_ATTRS, BODY_ATTRS, HEAD, BODY_SCRIPTS, resourceHints } = await this.metaRenderer.render(context)
|
const { HTML_ATTRS, BODY_ATTRS, HEAD, BODY_SCRIPTS, getPreloadFiles } = await this.metaRenderer.render(context)
|
||||||
const APP = `<div id="__nuxt">${this.resources.loadingHTML}</div>` + BODY_SCRIPTS
|
const APP = `<div id="__nuxt">${this.resources.loadingHTML}</div>` + BODY_SCRIPTS
|
||||||
|
|
||||||
// Detect 404 errors
|
// Detect 404 errors
|
||||||
@ -507,7 +297,7 @@ module.exports = class Renderer {
|
|||||||
ENV
|
ENV
|
||||||
})
|
})
|
||||||
|
|
||||||
return { html, resourceHints }
|
return { html, getPreloadFiles }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call renderToString from the bundleRenderer and generate the HTML (will update the context as well)
|
// Call renderToString from the bundleRenderer and generate the HTML (will update the context as well)
|
||||||
@ -522,12 +312,10 @@ module.exports = class Renderer {
|
|||||||
HEAD += `<base href="${this.options.router.base}">`
|
HEAD += `<base href="${this.options.router.base}">`
|
||||||
}
|
}
|
||||||
|
|
||||||
let resourceHints = ''
|
|
||||||
|
|
||||||
if (this.options.render.resourceHints) {
|
if (this.options.render.resourceHints) {
|
||||||
resourceHints = context.renderResourceHints()
|
HEAD += context.renderResourceHints()
|
||||||
HEAD += resourceHints
|
|
||||||
}
|
}
|
||||||
|
|
||||||
APP += `<script type="text/javascript">window.__NUXT__=${serialize(context.nuxt, { isJSON: true })};</script>`
|
APP += `<script type="text/javascript">window.__NUXT__=${serialize(context.nuxt, { isJSON: true })};</script>`
|
||||||
APP += context.renderScripts()
|
APP += context.renderScripts()
|
||||||
APP += m.script.text({ body: true })
|
APP += m.script.text({ body: true })
|
||||||
@ -544,7 +332,7 @@ module.exports = class Renderer {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
html,
|
html,
|
||||||
resourceHints,
|
getPreloadFiles: context.getPreloadFiles,
|
||||||
error: context.nuxt.error,
|
error: context.nuxt.error,
|
||||||
redirected: context.redirected
|
redirected: context.redirected
|
||||||
}
|
}
|
||||||
|
42
package.json
42
package.json
@ -56,21 +56,22 @@
|
|||||||
"npm": ">=5.0.0"
|
"npm": ">=5.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxtjs/youch": "^3.1.0",
|
"@nuxtjs/youch": "^4.0.1",
|
||||||
"ansi-html": "^0.0.7",
|
"ansi-html": "^0.0.7",
|
||||||
"autoprefixer": "^7.2.3",
|
"autoprefixer": "^7.2.4",
|
||||||
"babel-core": "^6.26.0",
|
"babel-core": "^6.26.0",
|
||||||
"babel-loader": "^7.1.2",
|
"babel-loader": "^7.1.2",
|
||||||
"babel-preset-vue-app": "^2.0.0",
|
"babel-preset-vue-app": "^2.0.0",
|
||||||
"caniuse-lite": "^1.0.30000783",
|
"caniuse-lite": "^1.0.30000787",
|
||||||
"chalk": "^2.3.0",
|
"chalk": "^2.3.0",
|
||||||
"chokidar": "^1.7.0",
|
"chokidar": "^2.0.0",
|
||||||
"clone": "^2.1.1",
|
"clone": "^2.1.1",
|
||||||
"compression": "^1.7.1",
|
"compression": "^1.7.1",
|
||||||
"connect": "^3.6.5",
|
"connect": "^3.6.5",
|
||||||
|
"css-hot-loader": "^1.3.5",
|
||||||
"css-loader": "^0.28.7",
|
"css-loader": "^0.28.7",
|
||||||
"debug": "^3.1.0",
|
"debug": "^3.1.0",
|
||||||
"es6-promise": "^4.1.1",
|
"es6-promise": "^4.2.2",
|
||||||
"etag": "^1.8.1",
|
"etag": "^1.8.1",
|
||||||
"extract-text-webpack-plugin": "^3.0.2",
|
"extract-text-webpack-plugin": "^3.0.2",
|
||||||
"file-loader": "^1.1.6",
|
"file-loader": "^1.1.6",
|
||||||
@ -79,7 +80,7 @@
|
|||||||
"fs-extra": "^5.0.0",
|
"fs-extra": "^5.0.0",
|
||||||
"glob": "^7.1.2",
|
"glob": "^7.1.2",
|
||||||
"hash-sum": "^1.0.2",
|
"hash-sum": "^1.0.2",
|
||||||
"html-minifier": "3.5.7",
|
"html-minifier": "3.5.8",
|
||||||
"html-webpack-plugin": "^2.30.1",
|
"html-webpack-plugin": "^2.30.1",
|
||||||
"lodash": "^4.17.4",
|
"lodash": "^4.17.4",
|
||||||
"lru-cache": "^4.1.1",
|
"lru-cache": "^4.1.1",
|
||||||
@ -87,10 +88,10 @@
|
|||||||
"minimist": "^1.2.0",
|
"minimist": "^1.2.0",
|
||||||
"open-in-editor": "^2.2.0",
|
"open-in-editor": "^2.2.0",
|
||||||
"opencollective": "^1.0.3",
|
"opencollective": "^1.0.3",
|
||||||
"postcss": "^6.0.14",
|
"postcss": "^6.0.15",
|
||||||
"postcss-cssnext": "^3.0.2",
|
"postcss-cssnext": "^3.0.2",
|
||||||
"postcss-import": "^11.0.0",
|
"postcss-import": "^11.0.0",
|
||||||
"postcss-loader": "^2.0.8",
|
"postcss-loader": "^2.0.10",
|
||||||
"postcss-url": "^7.3.0",
|
"postcss-url": "^7.3.0",
|
||||||
"pretty-error": "^2.1.1",
|
"pretty-error": "^2.1.1",
|
||||||
"progress-bar-webpack-plugin": "^1.10.0",
|
"progress-bar-webpack-plugin": "^1.10.0",
|
||||||
@ -99,31 +100,32 @@
|
|||||||
"server-destroy": "^1.0.1",
|
"server-destroy": "^1.0.1",
|
||||||
"source-map": "^0.6.1",
|
"source-map": "^0.6.1",
|
||||||
"style-resources-loader": "^1.0.0",
|
"style-resources-loader": "^1.0.0",
|
||||||
"uglifyjs-webpack-plugin": "^1.1.4",
|
"uglifyjs-webpack-plugin": "^1.1.5",
|
||||||
|
"upath": "^1.0.2",
|
||||||
"url-loader": "^0.6.2",
|
"url-loader": "^0.6.2",
|
||||||
"vue": "^2.5.11",
|
"vue": "^2.5.13",
|
||||||
"vue-loader": "^13.6.0",
|
"vue-loader": "^13.6.2",
|
||||||
"vue-meta": "^1.4.0",
|
"vue-meta": "^1.4.0",
|
||||||
"vue-router": "^3.0.1",
|
"vue-router": "^3.0.1",
|
||||||
"vue-server-renderer": "^2.5.11",
|
"vue-server-renderer": "^2.5.13",
|
||||||
"vue-template-compiler": "^2.5.11",
|
"vue-template-compiler": "^2.5.13",
|
||||||
"vuex": "^3.0.1",
|
"vuex": "^3.0.1",
|
||||||
"webpack": "^3.10.0",
|
"webpack": "^3.10.0",
|
||||||
"webpack-bundle-analyzer": "^2.9.0",
|
"webpack-bundle-analyzer": "^2.9.0",
|
||||||
"webpack-dev-middleware": "^2.0.1",
|
"webpack-dev-middleware": "^2.0.3",
|
||||||
"webpack-hot-middleware": "^2.21.0",
|
"webpack-hot-middleware": "^2.21.0",
|
||||||
"webpack-node-externals": "^1.6.0"
|
"webpack-node-externals": "^1.6.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ava": "^0.24.0",
|
"ava": "^0.24.0",
|
||||||
"babel-eslint": "^8.0.3",
|
"babel-eslint": "^8.1.2",
|
||||||
"babel-plugin-array-includes": "^2.0.3",
|
"babel-plugin-array-includes": "^2.0.3",
|
||||||
"babel-plugin-external-helpers": "^6.22.0",
|
"babel-plugin-external-helpers": "^6.22.0",
|
||||||
"babel-plugin-istanbul": "^4.1.5",
|
"babel-plugin-istanbul": "^4.1.5",
|
||||||
"codecov": "^3.0.0",
|
"codecov": "^3.0.0",
|
||||||
"copy-webpack-plugin": "^4.3.0",
|
"copy-webpack-plugin": "^4.3.1",
|
||||||
"cross-env": "^5.1.1",
|
"cross-env": "^5.1.3",
|
||||||
"eslint": "^4.13.1",
|
"eslint": "^4.14.0",
|
||||||
"eslint-config-standard": "^11.0.0-beta.0",
|
"eslint-config-standard": "^11.0.0-beta.0",
|
||||||
"eslint-config-standard-jsx": "^4.0.2",
|
"eslint-config-standard-jsx": "^4.0.2",
|
||||||
"eslint-plugin-html": "^4.0.1",
|
"eslint-plugin-html": "^4.0.1",
|
||||||
@ -136,12 +138,12 @@
|
|||||||
"finalhandler": "^1.1.0",
|
"finalhandler": "^1.1.0",
|
||||||
"jsdom": "^11.5.1",
|
"jsdom": "^11.5.1",
|
||||||
"json-loader": "^0.5.7",
|
"json-loader": "^0.5.7",
|
||||||
"nyc": "^11.3.0",
|
"nyc": "^11.4.1",
|
||||||
"puppeteer": "^0.13.0",
|
"puppeteer": "^0.13.0",
|
||||||
"request": "^2.83.0",
|
"request": "^2.83.0",
|
||||||
"request-promise-native": "^1.0.5",
|
"request-promise-native": "^1.0.5",
|
||||||
"sinon": "^4.1.2",
|
"sinon": "^4.1.2",
|
||||||
"uglify-js": "^3.2.2"
|
"uglify-js": "^3.3.4"
|
||||||
},
|
},
|
||||||
"collective": {
|
"collective": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
|
@ -148,21 +148,27 @@ test.serial('/error2', async t => {
|
|||||||
t.deepEqual(await page.nuxt.errorData(), { message: 'Custom error' })
|
t.deepEqual(await page.nuxt.errorData(), { message: 'Custom error' })
|
||||||
})
|
})
|
||||||
|
|
||||||
test.serial('/redirect2', async t => {
|
test.serial('/redirect-middleware', async t => {
|
||||||
await page.nuxt.navigate('/redirect2')
|
await page.nuxt.navigate('/redirect-middleware')
|
||||||
|
|
||||||
t.is(await page.$text('h1'), 'Index page')
|
t.is(await page.$text('h1'), 'Index page')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.serial('/redirect3', async t => {
|
test.serial('/redirect-external', async t => {
|
||||||
// New page for redirecting to external link.
|
// New page for redirecting to external link.
|
||||||
const page = await browser.page(url('/'))
|
const page = await browser.page(url('/'))
|
||||||
await page.nuxt.navigate('/redirect3', false)
|
await page.nuxt.navigate('/redirect-external', false)
|
||||||
await page.waitForFunction(() => window.location.href === 'https://nuxtjs.org/')
|
await page.waitForFunction(() => window.location.href === 'https://nuxtjs.org/')
|
||||||
page.close()
|
page.close()
|
||||||
t.pass()
|
t.pass()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test.serial('/redirect-name', async t => {
|
||||||
|
await page.nuxt.navigate('/redirect-name')
|
||||||
|
|
||||||
|
t.is(await page.$text('h1'), 'My component!')
|
||||||
|
})
|
||||||
|
|
||||||
test.serial('/no-ssr', async t => {
|
test.serial('/no-ssr', async t => {
|
||||||
await page.nuxt.navigate('/no-ssr')
|
await page.nuxt.navigate('/no-ssr')
|
||||||
|
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
import test from 'ava'
|
import test from 'ava'
|
||||||
import { resolve } from 'path'
|
import { resolve } from 'path'
|
||||||
import { intercept, release } from './helpers/console'
|
import { intercept, release } from './helpers/console'
|
||||||
import { Nuxt, Builder, Utils } from '..'
|
import { Nuxt, Builder } from '..'
|
||||||
import { truncateSync, readFileSync, writeFileSync } from 'fs'
|
|
||||||
|
|
||||||
const port = 4001
|
const port = 4001
|
||||||
const url = (route) => 'http://localhost:' + port + route
|
const url = (route) => 'http://localhost:' + port + route
|
||||||
const rootDir = resolve(__dirname, 'fixtures/basic')
|
const rootDir = resolve(__dirname, 'fixtures/basic')
|
||||||
const pluginPath = resolve(rootDir, 'plugins', 'watch.js')
|
|
||||||
const pluginContent = readFileSync(pluginPath)
|
|
||||||
|
|
||||||
let nuxt = null
|
let nuxt = null
|
||||||
|
|
||||||
@ -20,11 +17,11 @@ test.serial('Init Nuxt.js', async t => {
|
|||||||
dev: true,
|
dev: true,
|
||||||
build: {
|
build: {
|
||||||
stats: false,
|
stats: false,
|
||||||
profile: true
|
profile: true,
|
||||||
},
|
extractCSS: {
|
||||||
plugins: [
|
allChunks: true
|
||||||
'~/plugins/watch.js'
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const spies = await intercept({ log: true, stderr: true }, async () => {
|
const spies = await intercept({ log: true, stderr: true }, async () => {
|
||||||
@ -37,30 +34,14 @@ test.serial('Init Nuxt.js', async t => {
|
|||||||
t.true(spies.log.calledWithMatch('OPEN'))
|
t.true(spies.log.calledWithMatch('OPEN'))
|
||||||
})
|
})
|
||||||
|
|
||||||
test.serial('remove mixins in live reloading', async t => {
|
// TODO: enable test when style-loader.js:60 was resolved
|
||||||
const spies = await intercept({ log: true, error: true, stderr: true })
|
// test.serial('/extractCSS', async t => {
|
||||||
await nuxt.renderRoute(url('/'))
|
// const window = await nuxt.renderAndGetWindow(url('/extractCSS'))
|
||||||
t.true(spies.log.calledWith('I am mixin'))
|
// const html = window.document.head.innerHTML
|
||||||
|
// t.true(html.includes('vendor.css'))
|
||||||
truncateSync(pluginPath)
|
// t.true(!html.includes('30px'))
|
||||||
await new Promise(async (resolve, reject) => {
|
// t.is(window.getComputedStyle(window.document.body).getPropertyValue('font-size'), '30px')
|
||||||
let waitTimes = 0
|
// })
|
||||||
while (spies.log.neverCalledWithMatch(/Compiled successfully/)) {
|
|
||||||
if (waitTimes++ >= 20) {
|
|
||||||
t.fail('Dev server doesn\'t reload after 2000ms')
|
|
||||||
reject(Error())
|
|
||||||
}
|
|
||||||
await Utils.waitFor(100)
|
|
||||||
}
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
spies.log.reset()
|
|
||||||
|
|
||||||
await nuxt.renderRoute(url('/'))
|
|
||||||
t.true(spies.log.neverCalledWith('I am mixin'))
|
|
||||||
t.is(spies.error.getCall(0).args[0].statusCode, 404)
|
|
||||||
release()
|
|
||||||
})
|
|
||||||
|
|
||||||
test.serial('/stateless', async t => {
|
test.serial('/stateless', async t => {
|
||||||
const spies = await intercept()
|
const spies = await intercept()
|
||||||
@ -82,6 +63,5 @@ test.serial('/stateless', async t => {
|
|||||||
|
|
||||||
// Close server and ask nuxt to stop listening to file changes
|
// Close server and ask nuxt to stop listening to file changes
|
||||||
test.after.always('Closing server and nuxt.js', async t => {
|
test.after.always('Closing server and nuxt.js', async t => {
|
||||||
writeFileSync(pluginPath, pluginContent)
|
|
||||||
await nuxt.close()
|
await nuxt.close()
|
||||||
})
|
})
|
||||||
|
@ -53,11 +53,15 @@ test.serial('/stateless', async t => {
|
|||||||
|
|
||||||
test.serial('/css', async t => {
|
test.serial('/css', async t => {
|
||||||
const window = await nuxt.renderAndGetWindow(url('/css'))
|
const window = await nuxt.renderAndGetWindow(url('/css'))
|
||||||
|
|
||||||
|
const headHtml = window.document.head.innerHTML
|
||||||
|
t.true(headHtml.includes('.red{color:red}'))
|
||||||
|
|
||||||
const element = window.document.querySelector('.red')
|
const element = window.document.querySelector('.red')
|
||||||
t.not(element, null)
|
t.not(element, null)
|
||||||
t.is(element.textContent, 'This is red')
|
t.is(element.textContent, 'This is red')
|
||||||
t.is(element.className, 'red')
|
t.is(element.className, 'red')
|
||||||
t.is(window.getComputedStyle(element).color, 'red')
|
// t.is(window.getComputedStyle(element), 'red')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.serial('/stateful', async t => {
|
test.serial('/stateful', async t => {
|
||||||
|
@ -46,17 +46,25 @@ test('/stateless', async t => {
|
|||||||
*/
|
*/
|
||||||
test('/css', async t => {
|
test('/css', async t => {
|
||||||
const window = await nuxt.renderAndGetWindow(url('/css'))
|
const window = await nuxt.renderAndGetWindow(url('/css'))
|
||||||
|
|
||||||
|
const headHtml = window.document.head.innerHTML
|
||||||
|
t.true(headHtml.includes('color:red'))
|
||||||
|
|
||||||
const element = window.document.querySelector('.red')
|
const element = window.document.querySelector('.red')
|
||||||
t.not(element, null)
|
t.not(element, null)
|
||||||
t.is(element.textContent, 'This is red')
|
t.is(element.textContent, 'This is red')
|
||||||
t.is(element.className, 'red')
|
t.is(element.className, 'red')
|
||||||
t.is(window.getComputedStyle(element).color, 'red')
|
// t.is(window.getComputedStyle(element).color, 'red')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('/postcss', async t => {
|
test('/postcss', async t => {
|
||||||
const window = await nuxt.renderAndGetWindow(url('/css'))
|
const window = await nuxt.renderAndGetWindow(url('/css'))
|
||||||
const element = window.document.querySelector('div.red')
|
|
||||||
t.is(window.getComputedStyle(element)['background-color'], 'blue')
|
const headHtml = window.document.head.innerHTML
|
||||||
|
t.true(headHtml.includes('background-color:blue'))
|
||||||
|
|
||||||
|
// const element = window.document.querySelector('div.red')
|
||||||
|
// t.is(window.getComputedStyle(element)['background-color'], 'blue')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('/stateful', async t => {
|
test('/stateful', async t => {
|
||||||
@ -132,7 +140,7 @@ test('/redirect -> check redirected source', async t => {
|
|||||||
|
|
||||||
test('/redirect -> external link', async t => {
|
test('/redirect -> external link', async t => {
|
||||||
const headers = {}
|
const headers = {}
|
||||||
const { html } = await nuxt.renderRoute('/redirect3', {
|
const { html } = await nuxt.renderRoute('/redirect-external', {
|
||||||
res: {
|
res: {
|
||||||
setHeader(k, v) {
|
setHeader(k, v) {
|
||||||
headers[k] = v
|
headers[k] = v
|
||||||
@ -187,14 +195,21 @@ test.serial('/error-midd', async t => {
|
|||||||
t.true(errorSpy.notCalled)
|
t.true(errorSpy.notCalled)
|
||||||
})
|
})
|
||||||
|
|
||||||
test.serial('/redirect2', async t => {
|
test.serial('/redirect-middleware', async t => {
|
||||||
const errorSpy = await interceptError()
|
const errorSpy = await interceptError()
|
||||||
await rp(url('/redirect2')) // Should not console.error
|
await rp(url('/redirect-middleware')) // Should not console.error
|
||||||
release()
|
release()
|
||||||
// Don't display error since redirect returns a noopApp
|
// Don't display error since redirect returns a noopApp
|
||||||
t.true(errorSpy.notCalled)
|
t.true(errorSpy.notCalled)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('/redirect-name', async t => {
|
||||||
|
const { html, redirected } = await nuxt.renderRoute('/redirect-name')
|
||||||
|
t.true(html.includes('<div id="__nuxt"></div>'))
|
||||||
|
t.true(redirected.path === '/stateless')
|
||||||
|
t.true(redirected.status === 302)
|
||||||
|
})
|
||||||
|
|
||||||
test('/no-ssr', async t => {
|
test('/no-ssr', async t => {
|
||||||
const { html } = await nuxt.renderRoute('/no-ssr')
|
const { html } = await nuxt.renderRoute('/no-ssr')
|
||||||
t.true(html.includes('<div class="no-ssr-placeholder"><p>Loading...</p></div>'))
|
t.true(html.includes('<div class="no-ssr-placeholder"><p>Loading...</p></div>'))
|
||||||
@ -208,8 +223,6 @@ test('/no-ssr (client-side)', async t => {
|
|||||||
|
|
||||||
test('ETag Header', async t => {
|
test('ETag Header', async t => {
|
||||||
const { headers: { etag } } = await rp(url('/stateless'), { resolveWithFullResponse: true })
|
const { headers: { etag } } = await rp(url('/stateless'), { resolveWithFullResponse: true })
|
||||||
// Validate etag
|
|
||||||
t.regex(etag, /W\/".*"$/)
|
|
||||||
// Verify functionality
|
// Verify functionality
|
||||||
const error = await t.throws(rp(url('/stateless'), { headers: { 'If-None-Match': etag } }))
|
const error = await t.throws(rp(url('/stateless'), { headers: { 'If-None-Match': etag } }))
|
||||||
t.is(error.statusCode, 304)
|
t.is(error.statusCode, 304)
|
||||||
|
@ -25,18 +25,18 @@ test.serial('Init Nuxt.js', async t => {
|
|||||||
t.true(logSpy.calledWithMatch('OPEN'))
|
t.true(logSpy.calledWithMatch('OPEN'))
|
||||||
})
|
})
|
||||||
|
|
||||||
test.serial('/test/_open (open-in-editor)', async t => {
|
test.serial('/test/__open-in-editor (open-in-editor)', async t => {
|
||||||
const logSpy = await interceptLog()
|
const logSpy = await interceptLog()
|
||||||
const { body } = await rp(url('/test/_open?file=pages/index.vue'), { resolveWithFullResponse: true })
|
const { body } = await rp(url('/test/__open-in-editor?file=pages/index.vue'), { resolveWithFullResponse: true })
|
||||||
t.is(body, 'opened in editor!')
|
t.is(body, 'opened in editor!')
|
||||||
release()
|
release()
|
||||||
t.is(logSpy.getCall(0).args[0], '[open in editor]')
|
t.is(logSpy.getCall(0).args[0], '[open in editor]')
|
||||||
t.true(logSpy.calledOnce)
|
t.true(logSpy.calledOnce)
|
||||||
})
|
})
|
||||||
|
|
||||||
test.serial('/test/_open should return error (open-in-editor)', async t => {
|
test.serial('/test/__open-in-editor should return error (open-in-editor)', async t => {
|
||||||
const logSpy = await interceptLog()
|
const logSpy = await interceptLog()
|
||||||
const { body } = await rp(url('/test/_open?file='), { resolveWithFullResponse: true })
|
const { body } = await rp(url('/test/__open-in-editor?file='), { resolveWithFullResponse: true })
|
||||||
t.is(body, 'File is not specified')
|
t.is(body, 'File is not specified')
|
||||||
release()
|
release()
|
||||||
t.is(logSpy.getCall(0).args[0], '[open in editor]')
|
t.is(logSpy.getCall(0).args[0], '[open in editor]')
|
||||||
|
9
test/fixtures/basic/pages/extractCSS.vue
vendored
Normal file
9
test/fixtures/basic/pages/extractCSS.vue
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
</style>
|
11
test/fixtures/basic/pages/redirect-name.vue
vendored
Normal file
11
test/fixtures/basic/pages/redirect-name.vue
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<template>
|
||||||
|
<div>Redirecting...</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
fetch({ redirect }) {
|
||||||
|
return redirect({name: 'stateless'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
12
test/fixtures/basic/plugins/watch.js
vendored
12
test/fixtures/basic/plugins/watch.js
vendored
@ -1,12 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
|
|
||||||
const Plugin = {
|
|
||||||
install(Vue) {
|
|
||||||
Vue.mixin({
|
|
||||||
created() {
|
|
||||||
console.log('I am mixin') // eslint-disable-line no-console
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vue.use(Plugin)
|
|
2
test/fixtures/debug/nuxt.config.js
vendored
2
test/fixtures/debug/nuxt.config.js
vendored
@ -2,7 +2,7 @@ module.exports = {
|
|||||||
router: {
|
router: {
|
||||||
base: '/test/'
|
base: '/test/'
|
||||||
},
|
},
|
||||||
dev: true, // Needed for _open middleware
|
dev: true, // Needed for __open-in-editor middleware
|
||||||
debug: true,
|
debug: true,
|
||||||
editor: {
|
editor: {
|
||||||
cmd: 'echo',
|
cmd: 'echo',
|
||||||
|
20
test/fixtures/spa/pages/mounted.vue
vendored
Normal file
20
test/fixtures/spa/pages/mounted.vue
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<template>
|
||||||
|
<h1>Test: {{ test.text }}</h1>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
test: {
|
||||||
|
text: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.test = {
|
||||||
|
text: 'updated'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
5
test/fixtures/ssr/nuxt.config.js
vendored
5
test/fixtures/ssr/nuxt.config.js
vendored
@ -1,7 +1,10 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
dev: false,
|
dev: false,
|
||||||
render: {
|
render: {
|
||||||
resourceHints: false
|
resourceHints: false,
|
||||||
|
http2: {
|
||||||
|
push: true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
stats: false,
|
stats: false,
|
||||||
|
4
test/fixtures/with-config/nuxt.config.js
vendored
4
test/fixtures/with-config/nuxt.config.js
vendored
@ -23,6 +23,7 @@ module.exports = {
|
|||||||
layoutTransition: 'test',
|
layoutTransition: 'test',
|
||||||
loadingIndicator: 'circle',
|
loadingIndicator: 'circle',
|
||||||
offline: true,
|
offline: true,
|
||||||
|
extensions: 'ts',
|
||||||
plugins: [
|
plugins: [
|
||||||
'~/plugins/test.js',
|
'~/plugins/test.js',
|
||||||
{ src: '~/plugins/only-client.js', ssr: false }
|
{ src: '~/plugins/only-client.js', ssr: false }
|
||||||
@ -69,7 +70,8 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
render: {
|
render: {
|
||||||
http2: {
|
http2: {
|
||||||
push: true
|
push: true,
|
||||||
|
shouldPush: (file, type) => type === 'script'
|
||||||
},
|
},
|
||||||
bundleRenderer: {
|
bundleRenderer: {
|
||||||
shouldPreload: (file, type) => {
|
shouldPreload: (file, type) => {
|
||||||
|
@ -66,6 +66,12 @@ test.serial('/custom (call mounted and created once)', async t => {
|
|||||||
t.true(logSpy.withArgs('mounted').calledOnce)
|
t.true(logSpy.withArgs('mounted').calledOnce)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test.serial('/mounted', async t => {
|
||||||
|
const { html } = await renderRoute('/mounted')
|
||||||
|
|
||||||
|
t.true(html.includes('<h1>Test: updated</h1>'))
|
||||||
|
})
|
||||||
|
|
||||||
test('/_nuxt/ (access publicPath in spa mode)', async t => {
|
test('/_nuxt/ (access publicPath in spa mode)', async t => {
|
||||||
const { response: { statusCode, statusMessage } } = await t.throws(renderRoute('/_nuxt/'))
|
const { response: { statusCode, statusMessage } } = await t.throws(renderRoute('/_nuxt/'))
|
||||||
t.is(statusCode, 404)
|
t.is(statusCode, 404)
|
||||||
|
@ -2,6 +2,7 @@ import test from 'ava'
|
|||||||
import { resolve } from 'path'
|
import { resolve } from 'path'
|
||||||
import rp from 'request-promise-native'
|
import rp from 'request-promise-native'
|
||||||
import { Nuxt, Builder } from '..'
|
import { Nuxt, Builder } from '..'
|
||||||
|
import styleLoader from '../lib/builder/webpack/style-loader'
|
||||||
import { interceptLog, release } from './helpers/console'
|
import { interceptLog, release } from './helpers/console'
|
||||||
|
|
||||||
const port = 4007
|
const port = 4007
|
||||||
@ -182,10 +183,11 @@ test('Check /test.txt should return 404', async t => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('Check build.styleResources for style-resources-loader', async t => {
|
test('Check build.styleResources for style-resources-loader', async t => {
|
||||||
const loaders = builder.styleLoader('scss')
|
const loaders = styleLoader.call(builder, 'scss')
|
||||||
const loader = loaders.find(l => l.loader === 'style-resources-loader')
|
const loader = loaders.find(l => l.loader === 'style-resources-loader')
|
||||||
t.is(typeof loader, 'object')
|
t.is(typeof loader, 'object')
|
||||||
t.deepEqual(loader.options, {
|
t.deepEqual(loader.options, {
|
||||||
|
sourceMap: false,
|
||||||
patterns: [
|
patterns: [
|
||||||
'~/assets/pre-process.scss'
|
'~/assets/pre-process.scss'
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user