Skip to content

Commit

Permalink
fix: webpack start watcher too ealry
Browse files Browse the repository at this point in the history
  • Loading branch information
liximomo committed Apr 3, 2020
1 parent ef7db31 commit 0173772
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 63 deletions.
4 changes: 2 additions & 2 deletions packages/runtime-react/src/bundler/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IApi, Hooks } from "@shuvi/types";
import { IApi, IHookBundlerConfig } from "@shuvi/types";
// @ts-ignore
import AliasPlugin from "enhanced-resolve/lib/AliasPlugin";

Expand All @@ -7,7 +7,7 @@ export function config(api: IApi) {
const resolveUser = (m: string) =>
require.resolve(m, { paths: [api.paths.rootDir] });

api.tap<Hooks.IBundlerConfig>("bundler:config", {
api.tap<IHookBundlerConfig>("bundler:config", {
name: "runtime-react",
fn: config => {
// const oriExternal = config.get("externals");
Expand Down
6 changes: 3 additions & 3 deletions packages/runtime-react/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import { IApi, Runtime, Hooks } from "@shuvi/types";
import { IApi, Runtime, IHookAppRoutes, IHookAppRoutesFile } from "@shuvi/types";
import { resolveDistFile } from "./paths";
import { matchRoutes } from "./router/matchRoutes";
import { config as configBundler } from "./bundler/config";
Expand Down Expand Up @@ -42,13 +42,13 @@ class ReactRuntime implements Runtime.IRuntime<React.ComponentType<any>> {

configBundler(api);

api.tap<Hooks.IAppRoutes>("app:routes", {
api.tap<IHookAppRoutes>("app:routes", {
name: "runtime-react",
fn: (routes: RouteConfig[]) => modifyRoutes(routes)
});

// add necessary imports
api.tap<Hooks.IAppRoutesFile>("app:routes-file", {
api.tap<IHookAppRoutesFile>("app:routes-file", {
name: "runtime-react",
fn: fileContent => {
return `
Expand Down
39 changes: 31 additions & 8 deletions packages/shuvi/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import {
IConfig,
IApi,
ICallHookOpts,
IHookConfig,
IHookOpts,
Hooks as IHooks,
IEventAppReady,
IHookAppRoutes,
IHookAppRoutesFile,
ISpecifier
} from "@shuvi/types";
import { Service, IServiceMode, App, IRouteConfig, IFile } from "@shuvi/core";
Expand Down Expand Up @@ -76,18 +79,17 @@ export class Api implements IApi {
return this._resources;
}

tap<Config extends IHooks.IHookConfig>(
tap<Config extends IHookConfig>(
hook: Config["name"],
opts: IHookOpts<Config["initialValue"], Config["args"]>
) {
this._hooks.addHook(hook, opts);
}

async callHook<Config extends IHooks.IHookConfig>(
async callHook<Config extends IHookConfig>(
name: Config["name"],
...args: Config["args"]
): Promise<void>;
async callHook<Config extends IHooks.IHookConfig>(
async callHook<Config extends IHookConfig>(
options: ICallHookOpts<Config["name"], Config["initialValue"]>,
...args: Config["args"]
): Promise<Config["initialValue"]>;
Expand All @@ -96,6 +98,19 @@ export class Api implements IApi {
return this._hooks.callHook(options as any, ...args);
}

on<Config extends IHookConfig>(
event: Config["name"],
listener: (...args: Config["args"]) => void
) {
this._hooks.addHook(event, { name: "listener", fn: listener });
}
emitEvent<Config extends IHookConfig>(
name: Config["name"],
...args: Config["args"]
): void {
this._hooks.callHook({ name, parallel: true }, ...args);
}

setBootstrapModule(path: string) {
this._app.setBootstrapModule(path);
}
Expand All @@ -108,15 +123,15 @@ export class Api implements IApi {
// add fallback route
routes.push({
id: genRouteId("404"),
componentFile: this.resolveAppFile("pages", "404"),
componentFile: this.resolveAppFile("pages", "404")
});

routes = await this.callHook<IHooks.IAppRoutes>({
routes = await this.callHook<IHookAppRoutes>({
name: "app:routes",
initialValue: routes
});
let content = `export default ${serializeRoutes(routes)}`;
content = await this.callHook<IHooks.IAppRoutesFile>({
content = await this.callHook<IHookAppRoutesFile>({
name: "app:routes-file",
initialValue: content
});
Expand All @@ -131,6 +146,14 @@ export class Api implements IApi {
} else {
await this._app.build({ dir: this.paths.appDir });
}

// prevent webpack watch running too early
// https://github.com/webpack/webpack/issues/7997
await new Promise((resolve, reject) => {
setTimeout(resolve, 1000);
});

this.emitEvent<IEventAppReady>("app:ready");
}

addResoure(identifier: string, loader: () => any): void {
Expand Down
6 changes: 3 additions & 3 deletions packages/shuvi/src/bundler/bundler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Hooks } from "@shuvi/types";
import { IHookBundlerConfig } from "@shuvi/types";
import { logger } from "@shuvi/core";
import { inspect } from "util";
import webpack, {
Expand Down Expand Up @@ -60,7 +60,7 @@ class BundlerImpl {
node: false,
entry: getClientEntry(this._api)
});
clientChain = await this._api.callHook<Hooks.IBundlerConfig>(
clientChain = await this._api.callHook<IHookBundlerConfig>(
{
name: "bundler:config",
initialValue: clientChain
Expand All @@ -77,7 +77,7 @@ class BundlerImpl {
node: true,
entry: getServerEntry(this._api)
});
serverChain = await this._api.callHook<Hooks.IBundlerConfig>(
serverChain = await this._api.callHook<IHookBundlerConfig>(
{
name: "bundler:config",
initialValue: serverChain
Expand Down
1 change: 1 addition & 0 deletions packages/shuvi/src/cmds/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default async function main(argv: string[]) {

const config = await loadConfig();
const shuviApp = shuvi({ dev: true, config });

try {
await shuviApp.listen(port, host);
} catch (err) {
Expand Down
15 changes: 6 additions & 9 deletions packages/shuvi/src/lib/devMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Hooks } from "@shuvi/types";
import { IEventBundlerDone } from "@shuvi/types";
import ForkTsCheckerWebpackPlugin, {
createCodeframeFormatter
} from "@shuvi/toolpack/lib/utils/forkTsCheckerWebpackPlugin";
Expand Down Expand Up @@ -150,14 +150,11 @@ export async function getDevMiddleware({
const isSuccessful = !messages.errors.length && !messages.warnings.length;
if (isSuccessful) {
_log("Compiled successfully!");
await api.callHook<Hooks.IBuildDone>(
{ name: "build:done", parallel: true },
{
first: isFirstSuccessfulCompile,
name: compiler.name,
stats
}
);
await api.emitEvent<IEventBundlerDone>("bundler:done", {
first: isFirstSuccessfulCompile,
name: compiler.name,
stats
});
isFirstSuccessfulCompile = false;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/shuvi/src/lib/onDemandRouteManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Runtime, Hooks } from "@shuvi/types";
import { Runtime, IHookAppRoutes } from "@shuvi/types";
import ModuleReplacePlugin from "@shuvi/toolpack/lib/webpack/plugins/module-replace-plugin";
import { DevMiddleware } from "./devMiddleware";
import { runtime } from "../runtime";
Expand All @@ -22,7 +22,7 @@ export class OnDemandRouteManager {
public devMiddleware: DevMiddleware | null = null;

constructor(api: Api) {
api.tap<Hooks.IAppRoutes>("app:routes", {
api.tap<IHookAppRoutes>("app:routes", {
name: "OnDemandRouteManager",
fn: (routes: RouteConfig[]) => {
this._routes = routes;
Expand Down
31 changes: 15 additions & 16 deletions packages/shuvi/src/shuvi/shuvi.dev.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Hooks } from "@shuvi/types";
import { IEventBundlerDone } from "@shuvi/types";
import { IIncomingMessage, IServerResponse, INextFunction } from "@shuvi/core";
import { getProjectInfo } from "@shuvi/toolpack/lib/utils/typeScript";
import { getDevMiddleware } from "../lib/devMiddleware";
Expand Down Expand Up @@ -52,24 +52,23 @@ export default class ShuviDev extends Base {
[WEBPACK_CONFIG_CLIENT]: false,
[WEBPACK_CONFIG_SERVER]: false
};
this._api.tap<Hooks.IBuildDone>("build:done", {
name: "shuvi",
fn: ({ first, name }) => {
status[name] = true;
if (
first &&
status[WEBPACK_CONFIG_CLIENT] &&
status[WEBPACK_CONFIG_SERVER]
) {
const localUrl = `http://${
hostname === "0.0.0.0" ? "localhost" : hostname
}:${port}`;
console.log(`Ready on ${localUrl}`);
}
this._api.on<IEventBundlerDone>("bundler:done", ({ first, name }) => {
status[name] = true;
if (
first &&
status[WEBPACK_CONFIG_CLIENT] &&
status[WEBPACK_CONFIG_SERVER]
) {
const localUrl = `http://${
hostname === "0.0.0.0" ? "localhost" : hostname
}:${port}`;
console.log(`Ready on ${localUrl}`);
}
});

super.listen(port, hostname);
console.log("Starting the development server...");

return super.listen(port, hostname);
}

protected getServiceMode() {
Expand Down
22 changes: 16 additions & 6 deletions packages/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import {
} from "@shuvi/core";
import * as Runtime from "./src/runtime";
import * as Bundler from "./src/bundler";
import * as Hooks from "./src/hooks";
import { IHookConfig } from "./src/hooks";
import WebpackChain from "webpack-chain";
import webpack from "webpack";

export * from "./src/hooks";

export { webpack, WebpackChain };

export { Runtime, Bundler, Hooks, IFile, ISpecifier, ITemplateData };
export { Runtime, Bundler, IFile, ISpecifier, ITemplateData };

export type IRouterHistoryMode = "browser" | "hash" | "auto";

Expand Down Expand Up @@ -65,20 +67,28 @@ export interface IApi {
config: IConfig;
assetPublicPath: string;

tap<Config extends Hooks.IHookConfig>(
tap<Config extends IHookConfig>(
hook: Config["name"],
opts: IHookOpts<Config["initialValue"], Config["args"]>
): void;

callHook<Config extends Hooks.IHookConfig>(
callHook<Config extends IHookConfig>(
name: Config["name"],
...args: Config["args"]
): Promise<void>;
callHook<Config extends Hooks.IHookConfig>(
callHook<Config extends IHookConfig>(
options: ICallHookOpts<Config["name"], Config["initialValue"]>,
...args: Config["args"]
): Promise<Config["initialValue"]>;

on<Config extends IHookConfig>(
hook: Config["name"],
listener: (...args: Config["args"]) => void
): void;
emitEvent<Config extends IHookConfig>(
name: Config["name"],
...args: Config["args"]
): void;

addAppFile: typeof App.prototype.addFile;
addAppExport: typeof App.prototype.addExport;

Expand Down
36 changes: 22 additions & 14 deletions packages/types/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,56 +9,64 @@ export interface IHookConfig {
initialValue: any;
}

type IDefaultConfig = {
type IDefaultHookConfig = {
args: [];
initialValue: void;
};

type defineHook<Name extends string, Config extends Partial<IHookConfig>> = {
type defineHook<
Name extends string,
Config extends Partial<IHookConfig> = {}
> = {
name: Name;
} & {
[K in keyof Config]: Config[K];
} &
{
[K in Exclude<keyof IDefaultConfig, keyof Config>]: IDefaultConfig[K];
[K in Exclude<
keyof IDefaultHookConfig,
keyof Config
>]: IDefaultHookConfig[K];
};

export type IAppRoutes = defineHook<
export type IHookAppRoutes = defineHook<
"app:routes",
{
initialValue: IRouteConfig[];
}
>;

export type IAppRoutesFile = defineHook<
export type IHookAppRoutesFile = defineHook<
"app:routes-file",
{
initialValue: string;
}
>;

export type IBuildDone = defineHook<
"build:done",
export type IEventAppReady = defineHook<"app:ready">;

export type IHookBundlerConfig = defineHook<
"bundler:config",
{
initialValue: WebpackChain;
args: [
{
first: boolean;
name: string;
stats: webpack.Stats;
mode: IServiceMode;
webpack: typeof webpack;
}
];
}
>;

export type IBundlerConfig = defineHook<
"bundler:config",
export type IEventBundlerDone = defineHook<
"bundler:done",
{
initialValue: WebpackChain;
args: [
{
first: boolean;
name: string;
mode: IServiceMode;
webpack: typeof webpack;
stats: webpack.Stats;
}
];
}
Expand Down

0 comments on commit 0173772

Please sign in to comment.