Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/lavas-project/lavas
Browse files Browse the repository at this point in the history
  • Loading branch information
zoumiaojiang committed Jan 3, 2018
2 parents e12c4f6 + 52073d6 commit c78c051
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 55 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,22 @@

=======

## [2.1.8-rc.2] - 2018-1-3

### Changed

- [Feature] Add `build.skeleton` in config. In SPA, you can toggle skeleton feature by `build.skeleton.enable` and change `core/Skeleton.vue` with `build.skeleton.path`.
- [Feature] Inject some lines in `core/service-worker.js`:
- Auto prefix when using `publicPath`. eg. `importScripts('${publicPath}static/js/workbox-sw.prod.v2.1.2.js');`
- Auto add `workboxSW.router.registerNavigationRoute();` at the end of the file. You don't need to modify when switching between `SSR` and `SPA` manually.

## [2.1.8-rc.1] - 2018-1-2

### Changed

- [Fix] `cssExtract` in production mode.
- [Fix] Update to [email protected].
- [Breaking Change] Remove `entry` in config.

## [2.0.7-rc.4] - 2017-12-20

Expand Down
73 changes: 41 additions & 32 deletions core/builder/base-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {join} from 'path';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import SkeletonWebpackPlugin from 'vue-skeleton-webpack-plugin';

import {TEMPLATE_HTML, DEFAULT_ENTRY_NAME} from '../constants';
import {TEMPLATE_HTML, DEFAULT_ENTRY_NAME, DEFAULT_SKELETON_PATH} from '../constants';
import {assetsPath} from '../utils/path';
import * as JsonUtil from '../utils/json';
import templateUtil from '../utils/template';
Expand Down Expand Up @@ -190,56 +190,65 @@ export default class BaseBuilder {
* create a webpack config which will be compiled later
*
* @param {boolean} watcherEnabled enable watcher
* @return {Object} mpaConfig webpack config for MPA
* @return {Object} spaConfig webpack config for SPA
*/
async createMPAConfig(watcherEnabled) {
async createSPAConfig(watcherEnabled) {
let {globals, build, router} = this.config;
let entryName = DEFAULT_ENTRY_NAME;
let rootDir = globals.rootDir;

// create mpa config based on client config
let mpaConfig = this.webpackConfig.client();
let skeletonEntries = {};
// create spa config based on client config
let spaConfig = this.webpackConfig.client();

// set context and clear entries
mpaConfig.entry = {};
mpaConfig.name = 'mpaclient';
mpaConfig.context = rootDir;
spaConfig.entry = {};
spaConfig.name = 'mpaclient';
spaConfig.context = rootDir;

/**
* for each module needs prerendering, we will:
* 1. add a html-webpack-plugin to output a relative HTML file
* for SPA, we will:
* 1. add a html-webpack-plugin to output a HTML file
* 2. create an entry if a skeleton component is provided
*/
if (!build.ssr) {
// set client entry first
mpaConfig.entry[entryName] = [`./core/entry-client.js`];
spaConfig.entry[entryName] = [`./core/entry-client.js`];

// add html-webpack-plugin
await this.addHtmlPlugin(mpaConfig, entryName, router.baseUrl, watcherEnabled);
await this.addHtmlPlugin(spaConfig, entryName, router.baseUrl, watcherEnabled);

// if skeleton provided, we need to create an entry
let skeletonPath = join(rootDir, `core/Skeleton.vue`);
let skeletonImportPath = `@/core/Skeleton.vue`;
if (await pathExists(skeletonPath)) {
let entryPath = await this.writeSkeletonEntry(skeletonImportPath);
skeletonEntries[entryName] = [entryPath];
}
}
if (build.skeleton && build.skeleton.enable) {
let skeletonConfig;
let skeletonEntries = {};
let skeletonPath;
let skeletonImportPath;
let skeletonRelativePath = build.skeleton.path || DEFAULT_SKELETON_PATH;

skeletonPath = join(rootDir, skeletonRelativePath);
skeletonImportPath = `@/${skeletonRelativePath}`;

if (await pathExists(skeletonPath)) {

// marked as supported at this time
this.skeletonEnabled = true;

if (Object.keys(skeletonEntries).length) {
// when ssr skeleton, we need to extract css from js
let skeletonConfig = this.webpackConfig.server({cssExtract: true});
// remove vue-ssr-client plugin
skeletonConfig.plugins.pop();
skeletonConfig.entry = skeletonEntries;

// add skeleton plugin
mpaConfig.plugins.push(new SkeletonWebpackPlugin({
webpackConfig: skeletonConfig
}));
skeletonEntries[entryName] = [await this.writeSkeletonEntry(skeletonImportPath)];

// when ssr skeleton, we need to extract css from js
skeletonConfig = this.webpackConfig.server({cssExtract: true});
// remove vue-ssr-client plugin
skeletonConfig.plugins.pop();
skeletonConfig.entry = skeletonEntries;

// add skeleton plugin
spaConfig.plugins.push(new SkeletonWebpackPlugin({
webpackConfig: skeletonConfig
}));
}
}
}

return mpaConfig;
return spaConfig;
}
}
22 changes: 13 additions & 9 deletions core/builder/dev-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
import webpack from 'webpack';
import MFS from 'memory-fs';
import chokidar from 'chokidar';
import {readFileSync, pathExists} from 'fs-extra';
import {readFileSync} from 'fs-extra';
import {join, posix} from 'path';

import historyMiddleware from 'connect-history-api-fallback';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import SkeletonWebpackPlugin from 'vue-skeleton-webpack-plugin';

import {LAVAS_CONFIG_FILE, STORE_FILE, DEFAULT_ENTRY_NAME} from '../constants';
import {LAVAS_CONFIG_FILE, STORE_FILE, DEFAULT_ENTRY_NAME, DEFAULT_SKELETON_PATH} from '../constants';
import {enableHotReload, writeFileInDev} from '../utils/webpack';
import {routes2Reg} from '../utils/router';

Expand Down Expand Up @@ -80,11 +80,13 @@ export default class DevBuilder extends BaseBuilder {
* @param {Object} clientConfig webpack client config
*/
addSkeletonRoutes(clientConfig) {
let {globals, build} = this.config;
let skeletonRelativePath = build.skeleton && build.skeleton.path || DEFAULT_SKELETON_PATH;
clientConfig.module.rules.push(SkeletonWebpackPlugin.loader({
resource: [join(this.config.globals.rootDir, `.lavas/router`)],
resource: [join(globals.rootDir, `.lavas/router`)],
options: {
entry: [DEFAULT_ENTRY_NAME],
importTemplate: 'import [nameHash] from \'@/core/Skeleton.vue\';',
importTemplate: `import [nameHash] from '@/${skeletonRelativePath}';`,
routePathTemplate: '/skeleton-[name]',
insertAfter: 'let routes = ['
}
Expand Down Expand Up @@ -118,7 +120,7 @@ export default class DevBuilder extends BaseBuilder {
*/
async build() {
this.isDev = true;
let mpaConfig;
let spaConfig;
let clientConfig;
let serverConfig;
let hotMiddleware;
Expand Down Expand Up @@ -169,17 +171,19 @@ export default class DevBuilder extends BaseBuilder {
else {
console.log('[Lavas] SPA build starting...');
// create spa config first
mpaConfig = await this.createMPAConfig(true);
spaConfig = await this.createSPAConfig(true);

// enable hotreload in every entry in dev mode
await enableHotReload(this.lavasPath(), mpaConfig, true);
await enableHotReload(this.lavasPath(), spaConfig, true);

// add skeleton routes
this.addSkeletonRoutes(mpaConfig);
if (this.skeletonEnabled) {
this.addSkeletonRoutes(spaConfig);
}
}

// create a compiler based on mpa config
clientCompiler = webpack([clientConfig, mpaConfig].filter(config => config));
clientCompiler = webpack([clientConfig, spaConfig].filter(config => config));
clientCompiler.cache = this.sharedCache;

this.devMiddleware = webpackDevMiddleware(clientCompiler, {
Expand Down
2 changes: 1 addition & 1 deletion core/builder/prod-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export default class ProdBuilder extends BaseBuilder {
// SPA build process
else {
console.log('[Lavas] SPA build starting...');
await webpackCompile(await this.createMPAConfig());
await webpackCompile(await this.createSPAConfig());
console.log('[Lavas] SPA build completed.');
}
}
Expand Down
3 changes: 3 additions & 0 deletions core/config-reader.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ const DEFAULT_CONFIG = {
build: {
ssr: true,
publicPath: '/',
skeleton: {
enable: true
},
filenames: {
entry: 'js/[name].[chunkhash:8].js',
vendor: 'js/vendor.[chunkhash:8].js',
Expand Down
1 change: 1 addition & 0 deletions core/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export const TEMPLATE_HTML = 'index.html.tmpl';
export const CONFIG_FILE = 'config.json';
export const DEFAULT_ENTRY_NAME = 'index';
export const STORE_FILE = 'store.js';
export const DEFAULT_SKELETON_PATH = 'core/Skeleton.vue';
1 change: 1 addition & 0 deletions core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export default class LavasCore extends EventEmitter {
spinner.succeed(`[Lavas] ${this.env} build completed.`);
}
catch (e) {
console.log(e);
spinner.fail(`[Lavas] ${this.env} build failed.`)
}

Expand Down
48 changes: 47 additions & 1 deletion core/utils/workbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
* @file utils.workbox.js
* @author lavas
*/
import {basename} from 'path';
import {basename, join} from 'path';
import {readFileSync, writeFileSync} from 'fs-extra';
import WorkboxWebpackPlugin from 'workbox-webpack-plugin';

export const WORKBOX_PATH = require.resolve('workbox-sw');

Expand All @@ -20,3 +22,47 @@ export function getWorkboxFiles(isProd) {
`${filename}.map`
];
}

/**
* use workbox-webpack-plugin
*
* @param {Object} webpackConfig webpack config
* @param {Object} workboxConfig workbox config
* @param {Object} lavasConfig lavas config
*/
export function useWorkbox(webpackConfig, workboxConfig, lavasConfig) {
let {swSrc, appshellUrl} = workboxConfig;
let {buildVersion, build: {publicPath, ssr}, globals} = lavasConfig;

// service-worker provided by user
let serviceWorkerContent = readFileSync(swSrc);

// import workbox-sw
let importWorkboxClause = `importScripts('${publicPath}static/js/workbox-sw.prod.v2.1.2.js');`;
serviceWorkerContent = importWorkboxClause + serviceWorkerContent;

// register navigation in the end
let registerNavigationClause;
if (ssr) {
registerNavigationClause = `workboxSW.router.registerNavigationRoute('${appshellUrl}');`;

// add build version to templatedUrls
if (appshellUrl) {
workboxConfig.templatedUrls = {
[appshellUrl]: `${buildVersion}`
};
}
}
else {
registerNavigationClause = `workboxSW.router.registerNavigationRoute('/index.html');`;
}
serviceWorkerContent += registerNavigationClause;

// write new service worker in .lavas/sw.js
let tempSwSrc = join(globals.rootDir, './.lavas', 'sw-temp.js');
writeFileSync(tempSwSrc, serviceWorkerContent, 'utf8');
workboxConfig.swSrc = tempSwSrc;

// use [email protected]
webpackConfig.plugins.push(new WorkboxWebpackPlugin(workboxConfig));
}
15 changes: 4 additions & 11 deletions core/webpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ import CopyWebpackPlugin from 'copy-webpack-plugin';
import VueSSRServerPlugin from 'vue-server-renderer/server-plugin';
import {BundleAnalyzerPlugin} from 'webpack-bundle-analyzer';
import SWRegisterWebpackPlugin from 'sw-register-webpack-plugin';
import WorkboxWebpackPlugin from 'workbox-webpack-plugin';

import {vueLoaders, styleLoaders} from './utils/loader';
import {assetsPath} from './utils/path';
import {WORKBOX_PATH, getWorkboxFiles} from './utils/workbox';
import {WORKBOX_PATH, getWorkboxFiles, useWorkbox} from './utils/workbox';
import {LAVAS_DIRNAME_IN_DIST, SERVER_BUNDLE, ASSETS_DIRNAME_IN_DIST} from './constants';

import fs from 'fs';
Expand Down Expand Up @@ -168,7 +167,7 @@ export default class WebpackConfig {
* @return {Object} client base config
*/
client(buildConfig = {}) {
let {buildVersion, ssr, globals, build, manifest, serviceWorker: workboxConfig} = this.config;
let {buildVersion, globals, build, manifest, serviceWorker: workboxConfig} = this.config;

/* eslint-disable fecs-one-var-per-line */
let {publicPath, filenames, cssSourceMap, cssMinimize, cssExtract,
Expand Down Expand Up @@ -249,15 +248,9 @@ export default class WebpackConfig {
]
});

// Use workbox in prod mode.
if (this.isProd && workboxConfig) {
if (workboxConfig.appshellUrls && workboxConfig.appshellUrls.length) {
workboxConfig.templatedUrls = {};
workboxConfig.appshellUrls.forEach(appshellUrl => {
workboxConfig.templatedUrls[appshellUrl] = `${buildVersion}`;
});
}
clientConfig.plugins.push(new WorkboxWebpackPlugin(workboxConfig));
// Use [email protected] in prod mode.
useWorkbox(clientConfig, workboxConfig, this.config);
}

// Copy static files to /dist.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "lavas",
"version": "2.1.8-rc.1",
"version": "2.1.8-rc.2",
"description": "pwa project solution cli tool",
"main": "dist/core/index.js",
"files": [
Expand Down

0 comments on commit c78c051

Please sign in to comment.