From dc84429d67f5b7187183f5d1991b9f29391d99df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Chopin?= Date: Mon, 17 Aug 2020 20:02:10 +0200 Subject: [PATCH] chore: wip --- packages/nuxt3/src/builder/app.ts | 1 + packages/nuxt3/src/builder/builder.ts | 61 +++++++++++++++++-- packages/nuxt3/src/builder/template.ts | 56 ++++++++++------- packages/nuxt3/src/builder/watch.ts | 40 ++++++++++++ packages/nuxt3/src/webpack/config/base.ts | 7 ++- .../nuxt3/src/webpack/utils/style-loader.ts | 10 +-- 6 files changed, 143 insertions(+), 32 deletions(-) create mode 100644 packages/nuxt3/src/builder/watch.ts diff --git a/packages/nuxt3/src/builder/app.ts b/packages/nuxt3/src/builder/app.ts index 046d46fc62..fecd8e8973 100644 --- a/packages/nuxt3/src/builder/app.ts +++ b/packages/nuxt3/src/builder/app.ts @@ -1,4 +1,5 @@ import { resolve } from 'path' +import { createWatcher } from './watch' export interface NuxtRoute { path: '' diff --git a/packages/nuxt3/src/builder/builder.ts b/packages/nuxt3/src/builder/builder.ts index 2fcccd1184..a88f57a54a 100644 --- a/packages/nuxt3/src/builder/builder.ts +++ b/packages/nuxt3/src/builder/builder.ts @@ -1,11 +1,15 @@ +import { join } from 'path' +import fsExtra from 'fs-extra' import { BundleBuilder } from 'src/webpack' import { Nuxt } from '../core' -import { copyTemplates } from './template' +import { compileTemplates, scanTemplates, NuxtTemplate } from './template' +import { createWatcher } from './watch' import { resolveApp, NuxtApp } from './app' export class Builder { nuxt: Nuxt app: NuxtApp + templates: NuxtTemplate[] constructor (nuxt) { this.nuxt = nuxt @@ -17,15 +21,64 @@ export class Builder { } // Extends VueRouter -async function build (builder: Builder) { +async function build(builder: Builder) { const { nuxt } = builder - builder.app = resolveApp(nuxt, nuxt.options.srcDir) - await copyTemplates(builder) + await generate(builder) + + if (nuxt.options.dev) { + watch(builder) + } await bundle(builder) } +function watch(builder: Builder) { + const { nuxt } = builder + + // Watch internal templates + const nuxtAppWatcher = createWatcher(nuxt.options.appDir) + // nuxtAppWatcher.debug() + nuxtAppWatcher.watchAll(async () => { + console.log('Re-generate templates') + await compileTemplates(builder.templates, nuxt.options.buildDir) + }) + + // Watch user app + const appWatcher = createWatcher(builder.app.srcDir, { + ignored: [ + nuxt.options.buildDir + ] + }) + // appWatcher.debug() + appWatcher.watch(/(A|a)pp\.[a-z]{2,3}/, async () => { + await new Promise((resolve) => setTimeout(resolve, 200)) + await generate(builder) + }) + appWatcher.watch('pages/', async () => { + console.log('Re-generate routes') + await compileTemplates(builder.templates, nuxt.options.buildDir) + }) +} + +export async function generate(builder: Builder) { + const { nuxt } = builder + + await fsExtra.mkdirp(nuxt.options.buildDir) + builder.app = resolveApp(nuxt, nuxt.options.srcDir) + + const templatesDir = join(builder.nuxt.options.appDir, '_templates') + const appTemplates = await scanTemplates(templatesDir, { + app: builder.app + }) + + builder.templates = [ + ...appTemplates + ] + + await compileTemplates(builder.templates, nuxt.options.buildDir) +} + async function bundle ({ nuxt }: Builder) { // TODO: get rid of this context and directly pass nuxt to BundleBuilder const bundleBuilder = new BundleBuilder({ diff --git a/packages/nuxt3/src/builder/template.ts b/packages/nuxt3/src/builder/template.ts index d41e101e1b..d03b29795c 100644 --- a/packages/nuxt3/src/builder/template.ts +++ b/packages/nuxt3/src/builder/template.ts @@ -1,26 +1,40 @@ -import { resolve, join, sep } from 'path' +import { join, relative, dirname } from 'path' import fsExtra from 'fs-extra' import globby from 'globby' import lodashTemplate from 'lodash/template' -import { Builder } from './builder' -export async function copyTemplates ({ nuxt, app }: Builder) { - // Resolve appDir - const templatesDir = join(nuxt.options.appDir, '_templates') - - const templateFiles = (await globby(join(templatesDir, '/**'))) - .map(f => f.replace(templatesDir + sep, '')) - - await fsExtra.mkdirp(nuxt.options.buildDir) - - for (const template of templateFiles) { - const src = resolve(templatesDir, template) - const dst = resolve(nuxt.options.buildDir, template) - - const templateData = { app: Object.freeze(app) } - const srcContents = await fsExtra.readFile(src, 'utf-8') - const compiledSrc = lodashTemplate(srcContents, {})(templateData) - - await fsExtra.writeFile(dst, compiledSrc) - } +interface NuxtTemplate { + src: string // Absolute path to source file + path: string // Relative path of destination + data?: any +} + +async function compileTemplate({ src, path, data }: NuxtTemplate, destDir: string) { + const srcContents = await fsExtra.readFile(src, 'utf-8') + const compiledSrc = lodashTemplate(srcContents, {})(data) + const dest = join(destDir, path) + console.log('Compile template', dest) + await fsExtra.mkdirp(dirname(dest)) + await fsExtra.writeFile(dest, compiledSrc) +} + +export async function compileTemplates(templates: NuxtTemplate[], destDir: string) { + return Promise.all(templates.map(t => compileTemplate(t, destDir))) +} + +export async function scanTemplates (dir: string, data?: Object) { + const templateFiles = (await globby(join(dir, '/**'))) + + return templateFiles.map(src => ({ + src, + path: relative(dir, src), + data + })) +} + +export async function watchTemplate(template: NuxtTemplate, watcher: any, cb: Function) { + template.data = new Proxy(template.data, { + // TODO: deep watch option changes + }) + // TODO: Watch fs changes } diff --git a/packages/nuxt3/src/builder/watch.ts b/packages/nuxt3/src/builder/watch.ts new file mode 100644 index 0000000000..6ccc1f43e7 --- /dev/null +++ b/packages/nuxt3/src/builder/watch.ts @@ -0,0 +1,40 @@ +import chokidar, { WatchOptions } from 'chokidar' +import { relative } from 'path' + +export function createWatcher(dir: string|string[], options?: WatchOptions) { + const watcher = chokidar.watch(dir, { + ignored: [], + ignoreInitial: true, + ...options + }) + + const watchAll = (cb: Function, filter?: Function) => { + watcher.on('raw', (event, path: string, details) => { + if (options.ignored.find(ignore => path.match(ignore))) { + return // 🖕 chokidar ignored option + } + path = relative(dir, path) + const _event = { event, path } + if (!filter || filter(_event)) { + cb(_event) + } + }) + } + + const watch = (pattern: string| RegExp, cb: Function) => watchAll(cb, e => e.path.match(pattern)) + + const debug = (tag: string = '[Watcher]') => { + console.log(tag, 'Watching ', dir) + watchAll((e) => { + console.log(tag, e.event, e.path) + }) + } + + return { + watchAll, + watch, + debug + } +} + +export type Watcher = ReturnType diff --git a/packages/nuxt3/src/webpack/config/base.ts b/packages/nuxt3/src/webpack/config/base.ts index df719889ae..9aafcbe639 100644 --- a/packages/nuxt3/src/webpack/config/base.ts +++ b/packages/nuxt3/src/webpack/config/base.ts @@ -4,7 +4,8 @@ import TimeFixPlugin from 'time-fix-plugin' import cloneDeep from 'lodash/cloneDeep' import escapeRegExp from 'lodash/escapeRegExp' import VueLoaderPlugin from 'vue-loader/dist/pluginWebpack5' -import ExtractCssChunksPlugin from 'extract-css-chunks-webpack-plugin' +// import ExtractCssChunksPlugin from 'extract-css-chunks-webpack-plugin' +import MiniCssExtractPlugin from 'mini-css-extract-plugin' import TerserWebpackPlugin from 'terser-webpack-plugin' import WebpackBar from 'webpackbar' import env from 'std-env' @@ -398,7 +399,7 @@ export default class WebpackBaseConfig { // CSS extraction) if (buildOptions.extractCSS) { - plugins.push(new ExtractCssChunksPlugin(Object.assign({ + plugins.push(new MiniCssExtractPlugin(Object.assign({ filename: this.getFileName('css'), chunkFilename: this.getFileName('css') }, buildOptions.extractCSS))) @@ -446,7 +447,7 @@ export default class WebpackBaseConfig { // CSS extraction if (this.buildContext.buildOptions.extractCSS) { - plugins.push(new ExtractCssChunksPlugin(Object.assign({ + plugins.push(new MiniCssExtractPlugin(Object.assign({ filename: this.getFileName('css'), chunkFilename: this.getFileName('css'), // TODO: https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/issues/132 diff --git a/packages/nuxt3/src/webpack/utils/style-loader.ts b/packages/nuxt3/src/webpack/utils/style-loader.ts index 3a1962a83e..cee82cd886 100644 --- a/packages/nuxt3/src/webpack/utils/style-loader.ts +++ b/packages/nuxt3/src/webpack/utils/style-loader.ts @@ -1,5 +1,6 @@ import path from 'path' -import ExtractCssChunksPlugin from 'extract-css-chunks-webpack-plugin' +// import ExtractCssChunksPlugin from 'extract-css-chunks-webpack-plugin' +import MiniCssExtractPlugin from 'mini-css-extract-plugin' import { wrapArray } from 'src/utils' @@ -20,8 +21,9 @@ export default class StyleLoader { return this.buildContext.buildOptions.extractCSS } - get onlyLocals () { - return Boolean(this.isServer && this.extractCSS) + get onlyLocals() { + // Not supported anymore by css-loader + return false // Boolean(this.isServer && this.extractCSS) } normalize (loaders) { @@ -86,7 +88,7 @@ export default class StyleLoader { if (this.extractCSS) { const isDev = this.buildContext.options.dev return { - loader: ExtractCssChunksPlugin.loader, + loader: MiniCssExtractPlugin.loader, options: { // TODO: https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/issues/132 // https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/issues/161#issuecomment-500162574