chore: implement basic devServer

This commit is contained in:
Igor Randjelovic
2021-03-26 22:16:36 +01:00
parent bb23bca3ce
commit 75e6009b8a
7 changed files with 198 additions and 58 deletions

View 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
// });
// });
}

View File

@ -8,6 +8,7 @@ import path from 'path';
import fs from 'fs';
import { parseEnvFlags } from '../cli/parseEnvFlags';
import { run } from "./devServer";
const defaultConfig = path.resolve(
__dirname,
@ -43,9 +44,10 @@ program
program
.command('build')
.description('Build...')
.option('--env [name]', 'environment options')
.option('--hmr', 'enable HMR')
.option('--no-hmr', 'disable HMR')
.option('--env [name]', 'environment name')
.option('--config [path]', 'config path')
// .option('--hmr', 'enable HMR')
// .option('--no-hmr', 'disable HMR')
.option('--watch', 'watch for changes')
.allowUnknownOption()
.action((options, command) => {
@ -56,47 +58,69 @@ program
if (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: guard against invalid config
let configuration;
let configuration: webpack.Configuration;
try {
configuration = require(configPath)(env);
} catch (ignore) {
console.log(ignore);
} catch (err) {
console.log(err);
}
if (!configuration) {
console.log('No configuration!!!!!');
console.log('No configuration!');
return;
}
const compiler = webpack(configuration);
// todo: handle --watch flag
// todo: promisify callback?
compiler.watch(
{
ignored: ['platforms'],
},
(err, stats) => {
const webpackCompilationCallback = (err: webpack.WebpackError, stats: webpack.Stats) => {
if (err) {
// Do not keep cache anymore
compiler.purgeInputFileSystem();
console.error(err.stack || err);
if (err.details) {
console.error(err.details);
}
process.exitCode = 1;
return;
}
if (stats) {
console.log(
stats.toString({
chunks: false,
colors: true,
})
);
}
// err && console.log(err)
}
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);

View File

@ -1,4 +1,5 @@
import dedent from 'ts-dedent';
import { env } from "@nativescript/webpack";
// de-indents strings so multi-line string literals can be used
function cleanup(data: any[]) {
@ -28,8 +29,10 @@ export function warn(...data: any): void {
}
export function info(...data: any): void {
if(env.verbose) {
console.log(`[@nativescript/webpack] Info: \n`, ...cleanup(data));
}
}
// todo: refine
// export function error(message: string, info?: { possibleCauses?: string[] }) {

View File

@ -1,7 +1,7 @@
import { dirname, resolve } from 'path';
import { getPackageJson, getProjectRootPath } from './project';
import { error } from './log';
import { error, info } from './log';
import { env } from '../';
import AndroidPlatform from '../platforms/android';
@ -29,7 +29,7 @@ const platforms: {
* @param platform A platform definition of the platform specifics
*/
export function addPlatform(name: string, platform: INativeScriptPlatform) {
console.log('adding platform', name, platform);
info(`Adding platform ${name}`, platform);
platforms[name] = platform;
}

View File

@ -3,7 +3,13 @@
// todo: log correct message format for CLI to pick up
// 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__;
Promise.race(__NS_DEV_HOST_IPS__
@ -11,7 +17,7 @@ Promise.race(__NS_DEV_HOST_IPS__
.map(async url => {
await Http.request({
method: 'get',
url
url: url + 'ping'
})
return url;
@ -19,47 +25,63 @@ Promise.race(__NS_DEV_HOST_IPS__
__NS_DEV_HOST_URL__ = winner
})
let __SEQ = 0;
if (module.hot) {
module.hot.dispose(() => {
console.log('Disposing entry file?!')
// require('@nativescript/core').Application.resetRootView()
})
const orig = global.__onLiveSync
const log = (type, info) => {
console.log(`[nds] HMR ${type}:`, info)
// console.log(__NS_DEV_HOST_IPS__[0])
const send = (content: object) => {
if (__NS_DEV_HOST_URL__) {
Http.request({
method: 'post',
url: __NS_DEV_HOST_URL__,
content: JSON.stringify({
type,
info
})
content: JSON.stringify(content)
}).catch(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')
module.hot.addStatusHandler(status => {
log('status', status)
// sendStatus(status)
})
global.__onLiveSync = async function () {
// handle hot updated on LiveSync
console.log('~~~ livesynced ~~~')
// handle hot updates on LiveSync
console.log('~~~ livesync ~~~')
log('checking')
const hash = __webpack_require__.h();
await module.hot.check().catch(err => {
log('checking-failed', err)
sendStatus('failure', hash)
});
log('checked')
log('applying')
await module.hot.apply({
ignoreUnaccepted: false,
ignoreDeclined: false,
@ -67,9 +89,11 @@ if(module.hot) {
onDeclined(info) {
log('declined', info)
sendStatus('failure', hash);
},
onUnaccepted(info) {
log('unaccepted', info)
sendStatus('failure', hash);
},
onAccepted(info) {
log('accepted', info)
@ -79,10 +103,15 @@ if(module.hot) {
},
onErrored(info) {
log('errored', info)
sendStatus('failure', hash);
}
}).then(() => {
sendStatus('success', hash)
}).catch((err) => {
sendStatus('failure', hash)
log('applying-failed', err)
})
// log('applying')
// await module.hot.apply()
log('applying-done')

View File

@ -34,10 +34,6 @@ export class PlatformSuffixPlugin {
}
apply(compiler: any) {
console.log(
// this.extensions,
this.platform
);
const platformRE = new RegExp(`\.${this.platform}\.`);
// require.context

View File

@ -1,5 +1,6 @@
const id = 'WatchStatePlugin';
const version = 1;
const DEBUG = false;
export enum messages {
compilationComplete = 'Webpack compilation complete.',
@ -12,8 +13,6 @@ export enum messages {
* So the {N} CLI can get some idea when compilation completes.
*/
export class WatchStatePlugin {
isRunningWatching: boolean;
apply(compiler: any) {
let isWatchMode = false;
let prevAssets = [];
@ -21,8 +20,24 @@ export class WatchStatePlugin {
compiler.hooks.watchRun.tapAsync(id, function (compiler, callback) {
callback();
isWatchMode = true;
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;
});
compiler.hooks.afterEmit.tapAsync(id, function (compilation, callback) {
@ -53,21 +68,23 @@ export class WatchStatePlugin {
notify({
type: 'compilation',
version,
hash: compilation.hash,
data: {
emittedAssets,
staleAssets,
hash: compilation.hash,
}
});
});
}
}
function notify(message: any) {
DEBUG && console.log(`[${id}] Notify: `, message);
if (!process.send) {
return;
}
console.log(`[${id}] Notify: `, message);
process.send(message, (error) => {
if (error) {
console.error(`[${id}] Process Send Error: `, error);