mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-17 21:01:34 +08:00
chore: implement basic devServer
This commit is contained in:
71
packages/webpack5/src/bin/devServer.ts
Normal file
71
packages/webpack5/src/bin/devServer.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { createServer } from 'http'
|
||||||
|
// import { spawn } from 'child_process'
|
||||||
|
// yarn --cwd /Users/rigor789/Code/echo-server start
|
||||||
|
|
||||||
|
// let statuses: {
|
||||||
|
// [hash: string]: IHMRStatusData
|
||||||
|
// } = {}
|
||||||
|
|
||||||
|
export interface IHMRStatusData {
|
||||||
|
seq: number
|
||||||
|
uuid: string,
|
||||||
|
hash: string
|
||||||
|
status: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function run() {
|
||||||
|
createServer((req, res) => {
|
||||||
|
if (req.url === '/ping') {
|
||||||
|
console.log('PING -> PONG!')
|
||||||
|
return res.end("Pong.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.method !== 'POST') {
|
||||||
|
res.statusCode = 400;
|
||||||
|
return res.end("Unsupported method.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = "";
|
||||||
|
req.on("data", chunk => {
|
||||||
|
data += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on("end", () => {
|
||||||
|
try {
|
||||||
|
const signal = JSON.parse(data) as IHMRStatusData;
|
||||||
|
// if (!statuses[signal.hash] || statuses[signal.hash].seq < signal.seq) {
|
||||||
|
// statuses[signal.hash] = signal
|
||||||
|
// }
|
||||||
|
if (process.send) {
|
||||||
|
process.send({
|
||||||
|
type: 'hmr-status',
|
||||||
|
version: 1,
|
||||||
|
hash: signal.hash,
|
||||||
|
data: signal
|
||||||
|
}, (error) => {
|
||||||
|
if (error) {
|
||||||
|
console.error(`Process Send Error: `, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.end('ok.');
|
||||||
|
} catch (e) {
|
||||||
|
res.statusCode = 400;
|
||||||
|
res.end("Invalid JSON.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).listen(8238)
|
||||||
|
|
||||||
|
// spawn('/Users/rigor789/Code/echo-server/node_modules/.bin/ts-node', ['/Users/rigor789/Code/echo-server/index.ts'], {
|
||||||
|
// cwd: '/Users/rigor789/Code/echo-server/',
|
||||||
|
// stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
|
||||||
|
// }).on('message', function (data) {
|
||||||
|
// console.log({
|
||||||
|
// messageFromEchoServer: data
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import path from 'path';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
import { parseEnvFlags } from '../cli/parseEnvFlags';
|
import { parseEnvFlags } from '../cli/parseEnvFlags';
|
||||||
|
import { run } from "./devServer";
|
||||||
|
|
||||||
const defaultConfig = path.resolve(
|
const defaultConfig = path.resolve(
|
||||||
__dirname,
|
__dirname,
|
||||||
@ -43,9 +44,10 @@ program
|
|||||||
program
|
program
|
||||||
.command('build')
|
.command('build')
|
||||||
.description('Build...')
|
.description('Build...')
|
||||||
.option('--env [name]', 'environment options')
|
.option('--env [name]', 'environment name')
|
||||||
.option('--hmr', 'enable HMR')
|
.option('--config [path]', 'config path')
|
||||||
.option('--no-hmr', 'disable HMR')
|
// .option('--hmr', 'enable HMR')
|
||||||
|
// .option('--no-hmr', 'disable HMR')
|
||||||
.option('--watch', 'watch for changes')
|
.option('--watch', 'watch for changes')
|
||||||
.allowUnknownOption()
|
.allowUnknownOption()
|
||||||
.action((options, command) => {
|
.action((options, command) => {
|
||||||
@ -56,47 +58,69 @@ program
|
|||||||
if (options.env) {
|
if (options.env) {
|
||||||
env['env'] = options.env;
|
env['env'] = options.env;
|
||||||
}
|
}
|
||||||
// const env = {
|
|
||||||
// platform: 'ios',
|
|
||||||
// verbose: true,
|
|
||||||
// appResourcesPath: 'App_Resources',
|
|
||||||
// appPath: 'src'
|
|
||||||
// }
|
|
||||||
|
|
||||||
const configPath = path.resolve(process.cwd(), 'webpack.config.js');
|
const configPath = (() => {
|
||||||
|
if (options.config) {
|
||||||
|
return path.resolve(options.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.resolve(process.cwd(), 'webpack.config.js')
|
||||||
|
})();
|
||||||
|
|
||||||
// todo: validate config exists
|
// todo: validate config exists
|
||||||
// todo: guard against invalid config
|
// todo: guard against invalid config
|
||||||
let configuration;
|
let configuration: webpack.Configuration;
|
||||||
try {
|
try {
|
||||||
configuration = require(configPath)(env);
|
configuration = require(configPath)(env);
|
||||||
} catch (ignore) {
|
} catch (err) {
|
||||||
console.log(ignore);
|
console.log(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!configuration) {
|
if (!configuration) {
|
||||||
console.log('No configuration!!!!!');
|
console.log('No configuration!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const compiler = webpack(configuration);
|
const compiler = webpack(configuration);
|
||||||
|
|
||||||
// todo: handle --watch flag
|
const webpackCompilationCallback = (err: webpack.WebpackError, stats: webpack.Stats) => {
|
||||||
// todo: promisify callback?
|
if (err) {
|
||||||
compiler.watch(
|
// Do not keep cache anymore
|
||||||
{
|
compiler.purgeInputFileSystem();
|
||||||
ignored: ['platforms'],
|
|
||||||
},
|
console.error(err.stack || err);
|
||||||
(err, stats) => {
|
if (err.details) {
|
||||||
if (stats) {
|
console.error(err.details);
|
||||||
console.log(
|
|
||||||
stats.toString({
|
|
||||||
colors: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// err && console.log(err)
|
|
||||||
|
process.exitCode = 1;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
if (stats) {
|
||||||
|
console.log(
|
||||||
|
stats.toString({
|
||||||
|
chunks: false,
|
||||||
|
colors: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.watch) {
|
||||||
|
// run dev server
|
||||||
|
run();
|
||||||
|
|
||||||
|
console.log('webpack is watching the files...')
|
||||||
|
compiler.watch(
|
||||||
|
configuration.watchOptions ?? {},
|
||||||
|
webpackCompilationCallback
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
compiler.run(
|
||||||
|
webpackCompilationCallback
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
program.parse(process.argv);
|
program.parse(process.argv);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import dedent from 'ts-dedent';
|
import dedent from 'ts-dedent';
|
||||||
|
import { env } from "@nativescript/webpack";
|
||||||
|
|
||||||
// de-indents strings so multi-line string literals can be used
|
// de-indents strings so multi-line string literals can be used
|
||||||
function cleanup(data: any[]) {
|
function cleanup(data: any[]) {
|
||||||
@ -28,7 +29,9 @@ export function warn(...data: any): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function info(...data: any): void {
|
export function info(...data: any): void {
|
||||||
console.log(`[@nativescript/webpack] Info: \n`, ...cleanup(data));
|
if(env.verbose) {
|
||||||
|
console.log(`[@nativescript/webpack] Info: \n`, ...cleanup(data));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: refine
|
// todo: refine
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { dirname, resolve } from 'path';
|
import { dirname, resolve } from 'path';
|
||||||
|
|
||||||
import { getPackageJson, getProjectRootPath } from './project';
|
import { getPackageJson, getProjectRootPath } from './project';
|
||||||
import { error } from './log';
|
import { error, info } from './log';
|
||||||
import { env } from '../';
|
import { env } from '../';
|
||||||
|
|
||||||
import AndroidPlatform from '../platforms/android';
|
import AndroidPlatform from '../platforms/android';
|
||||||
@ -29,7 +29,7 @@ const platforms: {
|
|||||||
* @param platform A platform definition of the platform specifics
|
* @param platform A platform definition of the platform specifics
|
||||||
*/
|
*/
|
||||||
export function addPlatform(name: string, platform: INativeScriptPlatform) {
|
export function addPlatform(name: string, platform: INativeScriptPlatform) {
|
||||||
console.log('adding platform', name, platform);
|
info(`Adding platform ${name}`, platform);
|
||||||
platforms[name] = platform;
|
platforms[name] = platform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,13 @@
|
|||||||
// todo: log correct message format for CLI to pick up
|
// todo: log correct message format for CLI to pick up
|
||||||
// todo: build CLI service to listen for state changes
|
// todo: build CLI service to listen for state changes
|
||||||
// ---
|
// ---
|
||||||
import { Http } from '@nativescript/core'
|
|
||||||
|
import type { IHMRStatusData } from "../../bin/devServer";
|
||||||
|
import { Http, Device } from '@nativescript/core'
|
||||||
|
|
||||||
|
const uuid = Device.uuid;
|
||||||
|
|
||||||
|
console.log(`[HMR] uuid = ${uuid}`)
|
||||||
|
|
||||||
let __NS_DEV_HOST_URL__;
|
let __NS_DEV_HOST_URL__;
|
||||||
Promise.race(__NS_DEV_HOST_IPS__
|
Promise.race(__NS_DEV_HOST_IPS__
|
||||||
@ -11,7 +17,7 @@ Promise.race(__NS_DEV_HOST_IPS__
|
|||||||
.map(async url => {
|
.map(async url => {
|
||||||
await Http.request({
|
await Http.request({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url
|
url: url + 'ping'
|
||||||
})
|
})
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
@ -19,47 +25,63 @@ Promise.race(__NS_DEV_HOST_IPS__
|
|||||||
__NS_DEV_HOST_URL__ = winner
|
__NS_DEV_HOST_URL__ = winner
|
||||||
})
|
})
|
||||||
|
|
||||||
if(module.hot) {
|
let __SEQ = 0;
|
||||||
|
|
||||||
|
if (module.hot) {
|
||||||
module.hot.dispose(() => {
|
module.hot.dispose(() => {
|
||||||
console.log('Disposing entry file?!')
|
console.log('Disposing entry file?!')
|
||||||
// require('@nativescript/core').Application.resetRootView()
|
// require('@nativescript/core').Application.resetRootView()
|
||||||
})
|
})
|
||||||
|
|
||||||
const orig = global.__onLiveSync
|
const send = (content: object) => {
|
||||||
const log = (type, info) => {
|
if (__NS_DEV_HOST_URL__) {
|
||||||
console.log(`[nds] HMR ${type}:`, info)
|
|
||||||
// console.log(__NS_DEV_HOST_IPS__[0])
|
|
||||||
|
|
||||||
if(__NS_DEV_HOST_URL__) {
|
|
||||||
Http.request({
|
Http.request({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: __NS_DEV_HOST_URL__,
|
url: __NS_DEV_HOST_URL__,
|
||||||
content: JSON.stringify({
|
content: JSON.stringify(content)
|
||||||
type,
|
|
||||||
info
|
|
||||||
})
|
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sendStatus = (status, hash) => {
|
||||||
|
send({
|
||||||
|
seq: __SEQ++,
|
||||||
|
uuid,
|
||||||
|
hash,
|
||||||
|
status
|
||||||
|
} as IHMRStatusData)
|
||||||
|
}
|
||||||
|
|
||||||
|
const orig = global.__onLiveSync
|
||||||
|
const log = (type, info) => {
|
||||||
|
// console.log(`[nds] HMR ${type}:`, info)
|
||||||
|
}
|
||||||
|
|
||||||
log('init')
|
log('init')
|
||||||
|
|
||||||
module.hot.addStatusHandler(status => {
|
module.hot.addStatusHandler(status => {
|
||||||
log('status', status)
|
log('status', status)
|
||||||
|
// sendStatus(status)
|
||||||
})
|
})
|
||||||
|
|
||||||
global.__onLiveSync = async function () {
|
global.__onLiveSync = async function () {
|
||||||
// handle hot updated on LiveSync
|
// handle hot updates on LiveSync
|
||||||
console.log('~~~ livesynced ~~~')
|
console.log('~~~ livesync ~~~')
|
||||||
|
|
||||||
log('checking')
|
log('checking')
|
||||||
|
|
||||||
|
const hash = __webpack_require__.h();
|
||||||
|
|
||||||
await module.hot.check().catch(err => {
|
await module.hot.check().catch(err => {
|
||||||
log('checking-failed', err)
|
log('checking-failed', err)
|
||||||
|
sendStatus('failure', hash)
|
||||||
});
|
});
|
||||||
|
|
||||||
log('checked')
|
log('checked')
|
||||||
log('applying')
|
log('applying')
|
||||||
|
|
||||||
await module.hot.apply({
|
await module.hot.apply({
|
||||||
ignoreUnaccepted: false,
|
ignoreUnaccepted: false,
|
||||||
ignoreDeclined: false,
|
ignoreDeclined: false,
|
||||||
@ -67,9 +89,11 @@ if(module.hot) {
|
|||||||
|
|
||||||
onDeclined(info) {
|
onDeclined(info) {
|
||||||
log('declined', info)
|
log('declined', info)
|
||||||
|
sendStatus('failure', hash);
|
||||||
},
|
},
|
||||||
onUnaccepted(info) {
|
onUnaccepted(info) {
|
||||||
log('unaccepted', info)
|
log('unaccepted', info)
|
||||||
|
sendStatus('failure', hash);
|
||||||
},
|
},
|
||||||
onAccepted(info) {
|
onAccepted(info) {
|
||||||
log('accepted', info)
|
log('accepted', info)
|
||||||
@ -79,10 +103,15 @@ if(module.hot) {
|
|||||||
},
|
},
|
||||||
onErrored(info) {
|
onErrored(info) {
|
||||||
log('errored', info)
|
log('errored', info)
|
||||||
|
sendStatus('failure', hash);
|
||||||
}
|
}
|
||||||
|
}).then(() => {
|
||||||
|
sendStatus('success', hash)
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
sendStatus('failure', hash)
|
||||||
log('applying-failed', err)
|
log('applying-failed', err)
|
||||||
})
|
})
|
||||||
|
|
||||||
// log('applying')
|
// log('applying')
|
||||||
// await module.hot.apply()
|
// await module.hot.apply()
|
||||||
log('applying-done')
|
log('applying-done')
|
||||||
|
@ -34,10 +34,6 @@ export class PlatformSuffixPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
apply(compiler: any) {
|
apply(compiler: any) {
|
||||||
console.log(
|
|
||||||
// this.extensions,
|
|
||||||
this.platform
|
|
||||||
);
|
|
||||||
const platformRE = new RegExp(`\.${this.platform}\.`);
|
const platformRE = new RegExp(`\.${this.platform}\.`);
|
||||||
|
|
||||||
// require.context
|
// require.context
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
const id = 'WatchStatePlugin';
|
const id = 'WatchStatePlugin';
|
||||||
const version = 1;
|
const version = 1;
|
||||||
|
const DEBUG = false;
|
||||||
|
|
||||||
export enum messages {
|
export enum messages {
|
||||||
compilationComplete = 'Webpack compilation complete.',
|
compilationComplete = 'Webpack compilation complete.',
|
||||||
@ -12,8 +13,6 @@ export enum messages {
|
|||||||
* So the {N} CLI can get some idea when compilation completes.
|
* So the {N} CLI can get some idea when compilation completes.
|
||||||
*/
|
*/
|
||||||
export class WatchStatePlugin {
|
export class WatchStatePlugin {
|
||||||
isRunningWatching: boolean;
|
|
||||||
|
|
||||||
apply(compiler: any) {
|
apply(compiler: any) {
|
||||||
let isWatchMode = false;
|
let isWatchMode = false;
|
||||||
let prevAssets = [];
|
let prevAssets = [];
|
||||||
@ -21,8 +20,24 @@ export class WatchStatePlugin {
|
|||||||
compiler.hooks.watchRun.tapAsync(id, function (compiler, callback) {
|
compiler.hooks.watchRun.tapAsync(id, function (compiler, callback) {
|
||||||
callback();
|
callback();
|
||||||
|
|
||||||
|
if (isWatchMode) {
|
||||||
|
console.log(messages.changeDetected);
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
if (compiler.modifiedFiles) {
|
||||||
|
Array.from(compiler.modifiedFiles).forEach(file => {
|
||||||
|
console.log(`MODIFIED: ${file}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compiler.removedFiles) {
|
||||||
|
Array.from(compiler.removedFiles).forEach(file => {
|
||||||
|
console.log(`REMOVED: ${file}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
isWatchMode = true;
|
isWatchMode = true;
|
||||||
console.log(messages.changeDetected);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
compiler.hooks.afterEmit.tapAsync(id, function (compilation, callback) {
|
compiler.hooks.afterEmit.tapAsync(id, function (compilation, callback) {
|
||||||
@ -53,21 +68,23 @@ export class WatchStatePlugin {
|
|||||||
notify({
|
notify({
|
||||||
type: 'compilation',
|
type: 'compilation',
|
||||||
version,
|
version,
|
||||||
|
|
||||||
emittedAssets,
|
|
||||||
staleAssets,
|
|
||||||
hash: compilation.hash,
|
hash: compilation.hash,
|
||||||
|
|
||||||
|
data: {
|
||||||
|
emittedAssets,
|
||||||
|
staleAssets,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function notify(message: any) {
|
function notify(message: any) {
|
||||||
|
DEBUG && console.log(`[${id}] Notify: `, message);
|
||||||
if (!process.send) {
|
if (!process.send) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[${id}] Notify: `, message);
|
|
||||||
process.send(message, (error) => {
|
process.send(message, (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(`[${id}] Process Send Error: `, error);
|
console.error(`[${id}] Process Send Error: `, error);
|
||||||
|
Reference in New Issue
Block a user