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 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);

View File

@ -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

View File

@ -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;
} }

View File

@ -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')

View File

@ -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

View File

@ -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);