Files
ihrm/node_modules/@stencil/core/dev-server/server-process.js

1800 lines
97 KiB
JavaScript
Raw Normal View History

2023-05-10 15:06:57 +05:30
/*!
Stencil Dev Server Process v2.22.3 | MIT Licensed | https://stenciljs.com
*/
'use strict';
const index_js = require('../sys/node/index.js');
const path = require('path');
const childProcess = require('child_process');
const fs$1 = require('fs');
const os = require('os');
const fs$2 = require('../sys/node/graceful-fs.js');
const util = require('util');
const http = require('http');
const https = require('https');
const net = require('net');
const openInEditorApi = require('./open-in-editor-api.js');
const buffer = require('buffer');
const zlib = require('zlib');
const ws = require('./ws.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () {
return e[k];
}
});
}
});
}
n['default'] = e;
return Object.freeze(n);
}
const path__default = /*#__PURE__*/_interopDefaultLegacy(path);
const childProcess__default = /*#__PURE__*/_interopDefaultLegacy(childProcess);
const fs__default = /*#__PURE__*/_interopDefaultLegacy(fs$1);
const os__default = /*#__PURE__*/_interopDefaultLegacy(os);
const fs__default$1 = /*#__PURE__*/_interopDefaultLegacy(fs$2);
const util__default = /*#__PURE__*/_interopDefaultLegacy(util);
const http__namespace = /*#__PURE__*/_interopNamespace(http);
const https__namespace = /*#__PURE__*/_interopNamespace(https);
const net__namespace = /*#__PURE__*/_interopNamespace(net);
const openInEditorApi__default = /*#__PURE__*/_interopDefaultLegacy(openInEditorApi);
const zlib__namespace = /*#__PURE__*/_interopNamespace(zlib);
const ws__namespace = /*#__PURE__*/_interopNamespace(ws);
const noop = () => {
/* noop*/
};
const isFunction = (v) => typeof v === 'function';
const isString = (v) => typeof v === 'string';
/**
* Builds a diagnostic from an `Error`, appends it to the `diagnostics` parameter, and returns the created diagnostic
* @param diagnostics the series of diagnostics the newly created diagnostics should be added to
* @param err the error to derive information from in generating the diagnostic
* @param msg an optional message to use in place of `err` to generate the diagnostic
* @returns the generated diagnostic
*/
const catchError = (diagnostics, err, msg) => {
const diagnostic = {
level: 'error',
type: 'build',
header: 'Build Error',
messageText: 'build error',
relFilePath: null,
absFilePath: null,
lines: [],
};
if (isString(msg)) {
diagnostic.messageText = msg.length ? msg : 'UNKNOWN ERROR';
}
else if (err != null) {
if (err.stack != null) {
diagnostic.messageText = err.stack.toString();
}
else {
if (err.message != null) {
diagnostic.messageText = err.message.length ? err.message : 'UNKNOWN ERROR';
}
else {
diagnostic.messageText = err.toString();
}
}
}
if (diagnostics != null && !shouldIgnoreError(diagnostic.messageText)) {
diagnostics.push(diagnostic);
}
return diagnostic;
};
const shouldIgnoreError = (msg) => {
return msg === TASK_CANCELED_MSG;
};
const TASK_CANCELED_MSG = `task canceled`;
/**
* Convert Windows backslash paths to slash paths: foo\\bar foo/bar
* Forward-slash paths can be used in Windows as long as they're not
* extended-length paths and don't contain any non-ascii characters.
* This was created since the path methods in Node.js outputs \\ paths on Windows.
* @param path the Windows-based path to convert
* @returns the converted path
*/
const normalizePath = (path) => {
if (typeof path !== 'string') {
throw new Error(`invalid path to normalize`);
}
path = normalizeSlashes(path.trim());
const components = pathComponents(path, getRootLength(path));
const reducedComponents = reducePathComponents(components);
const rootPart = reducedComponents[0];
const secondPart = reducedComponents[1];
const normalized = rootPart + reducedComponents.slice(1).join('/');
if (normalized === '') {
return '.';
}
if (rootPart === '' &&
secondPart &&
path.includes('/') &&
!secondPart.startsWith('.') &&
!secondPart.startsWith('@')) {
return './' + normalized;
}
return normalized;
};
const normalizeSlashes = (path) => path.replace(backslashRegExp, '/');
const altDirectorySeparator = '\\';
const urlSchemeSeparator = '://';
const backslashRegExp = /\\/g;
const reducePathComponents = (components) => {
if (!Array.isArray(components) || components.length === 0) {
return [];
}
const reduced = [components[0]];
for (let i = 1; i < components.length; i++) {
const component = components[i];
if (!component)
continue;
if (component === '.')
continue;
if (component === '..') {
if (reduced.length > 1) {
if (reduced[reduced.length - 1] !== '..') {
reduced.pop();
continue;
}
}
else if (reduced[0])
continue;
}
reduced.push(component);
}
return reduced;
};
const getRootLength = (path) => {
const rootLength = getEncodedRootLength(path);
return rootLength < 0 ? ~rootLength : rootLength;
};
const getEncodedRootLength = (path) => {
if (!path)
return 0;
const ch0 = path.charCodeAt(0);
// POSIX or UNC
if (ch0 === 47 /* CharacterCodes.slash */ || ch0 === 92 /* CharacterCodes.backslash */) {
if (path.charCodeAt(1) !== ch0)
return 1; // POSIX: "/" (or non-normalized "\")
const p1 = path.indexOf(ch0 === 47 /* CharacterCodes.slash */ ? '/' : altDirectorySeparator, 2);
if (p1 < 0)
return path.length; // UNC: "//server" or "\\server"
return p1 + 1; // UNC: "//server/" or "\\server\"
}
// DOS
if (isVolumeCharacter(ch0) && path.charCodeAt(1) === 58 /* CharacterCodes.colon */) {
const ch2 = path.charCodeAt(2);
if (ch2 === 47 /* CharacterCodes.slash */ || ch2 === 92 /* CharacterCodes.backslash */)
return 3; // DOS: "c:/" or "c:\"
if (path.length === 2)
return 2; // DOS: "c:" (but not "c:d")
}
// URL
const schemeEnd = path.indexOf(urlSchemeSeparator);
if (schemeEnd !== -1) {
const authorityStart = schemeEnd + urlSchemeSeparator.length;
const authorityEnd = path.indexOf('/', authorityStart);
if (authorityEnd !== -1) {
// URL: "file:///", "file://server/", "file://server/path"
// For local "file" URLs, include the leading DOS volume (if present).
// Per https://www.ietf.org/rfc/rfc1738.txt, a host of "" or "localhost" is a
// special case interpreted as "the machine from which the URL is being interpreted".
const scheme = path.slice(0, schemeEnd);
const authority = path.slice(authorityStart, authorityEnd);
if (scheme === 'file' &&
(authority === '' || authority === 'localhost') &&
isVolumeCharacter(path.charCodeAt(authorityEnd + 1))) {
const volumeSeparatorEnd = getFileUrlVolumeSeparatorEnd(path, authorityEnd + 2);
if (volumeSeparatorEnd !== -1) {
if (path.charCodeAt(volumeSeparatorEnd) === 47 /* CharacterCodes.slash */) {
// URL: "file:///c:/", "file://localhost/c:/", "file:///c%3a/", "file://localhost/c%3a/"
return ~(volumeSeparatorEnd + 1);
}
if (volumeSeparatorEnd === path.length) {
// URL: "file:///c:", "file://localhost/c:", "file:///c$3a", "file://localhost/c%3a"
// but not "file:///c:d" or "file:///c%3ad"
return ~volumeSeparatorEnd;
}
}
}
return ~(authorityEnd + 1); // URL: "file://server/", "http://server/"
}
return ~path.length; // URL: "file://server", "http://server"
}
// relative
return 0;
};
const isVolumeCharacter = (charCode) => (charCode >= 97 /* CharacterCodes.a */ && charCode <= 122 /* CharacterCodes.z */) ||
(charCode >= 65 /* CharacterCodes.A */ && charCode <= 90 /* CharacterCodes.Z */);
const getFileUrlVolumeSeparatorEnd = (url, start) => {
const ch0 = url.charCodeAt(start);
if (ch0 === 58 /* CharacterCodes.colon */)
return start + 1;
if (ch0 === 37 /* CharacterCodes.percent */ && url.charCodeAt(start + 1) === 51 /* CharacterCodes._3 */) {
const ch2 = url.charCodeAt(start + 2);
if (ch2 === 97 /* CharacterCodes.a */ || ch2 === 65 /* CharacterCodes.A */)
return start + 3;
}
return -1;
};
const pathComponents = (path, rootLength) => {
const root = path.substring(0, rootLength);
const rest = path.substring(rootLength).split('/');
const restLen = rest.length;
if (restLen > 0 && !rest[restLen - 1]) {
rest.pop();
}
return [root, ...rest];
};
const DEV_SERVER_URL = '/~dev-server';
const DEV_MODULE_URL = '/~dev-module';
const DEV_SERVER_INIT_URL = `${DEV_SERVER_URL}-init`;
const OPEN_IN_EDITOR_URL = `${DEV_SERVER_URL}-open-in-editor`;
const version = '2.22.3';
const contentTypes = {"123":"application/vnd.lotus-1-2-3","1km":"application/vnd.1000minds.decision-model+xml","3dml":"text/vnd.in3d.3dml","3ds":"image/x-3ds","3g2":"video/3gpp2","3gp":"video/3gpp","3gpp":"video/3gpp","3mf":"model/3mf","7z":"application/x-7z-compressed","aab":"application/x-authorware-bin","aac":"audio/x-aac","aam":"application/x-authorware-map","aas":"application/x-authorware-seg","abw":"application/x-abiword","ac":"application/vnd.nokia.n-gage.ac+xml","acc":"application/vnd.americandynamics.acc","ace":"application/x-ace-compressed","acu":"application/vnd.acucobol","acutc":"application/vnd.acucorp","adp":"audio/adpcm","aep":"application/vnd.audiograph","afm":"application/x-font-type1","afp":"application/vnd.ibm.modcap","age":"application/vnd.age","ahead":"application/vnd.ahead.space","ai":"application/postscript","aif":"audio/x-aiff","aifc":"audio/x-aiff","aiff":"audio/x-aiff","air":"application/vnd.adobe.air-application-installer-package+zip","ait":"application/vnd.dvb.ait","ami":"application/vnd.amiga.ami","amr":"audio/amr","apk":"application/vnd.android.package-archive","apng":"image/apng","appcache":"text/cache-manifest","application":"application/x-ms-application","apr":"application/vnd.lotus-approach","arc":"application/x-freearc","arj":"application/x-arj","asc":"application/pgp-signature","asf":"video/x-ms-asf","asm":"text/x-asm","aso":"application/vnd.accpac.simply.aso","asx":"video/x-ms-asf","atc":"application/vnd.acucorp","atom":"application/atom+xml","atomcat":"application/atomcat+xml","atomdeleted":"application/atomdeleted+xml","atomsvc":"application/atomsvc+xml","atx":"application/vnd.antix.game-component","au":"audio/basic","avci":"image/avci","avcs":"image/avcs","avi":"video/x-msvideo","avif":"image/avif","aw":"application/applixware","azf":"application/vnd.airzip.filesecure.azf","azs":"application/vnd.airzip.filesecure.azs","azv":"image/vnd.airzip.accelerator.azv","azw":"application/vnd.amazon.ebook","b16":"image/vnd.pco.b16","bat":"application/x-msdownload","bcpio":"application/x-bcpio","bdf":"application/x-font-bdf","bdm":"application/vnd.syncml.dm+wbxml","bdoc":"application/x-bdoc","bed":"application/vnd.realvnc.bed","bh2":"application/vnd.fujitsu.oasysprs","bin":"application/octet-stream","blb":"application/x-blorb","blorb":"application/x-blorb","bmi":"application/vnd.bmi","bmml":"application/vnd.balsamiq.bmml+xml","bmp":"image/x-ms-bmp","book":"application/vnd.framemaker","box":"application/vnd.previewsystems.box","boz":"application/x-bzip2","bpk":"application/octet-stream","bsp":"model/vnd.valve.source.compiled-map","btif":"image/prs.btif","buffer":"application/octet-stream","bz":"application/x-bzip","bz2":"application/x-bzip2","c":"text/x-c","c11amc":"application/vnd.cluetrust.cartomobile-config","c11amz":"application/vnd.cluetrust.cartomobile-config-pkg","c4d":"application/vnd.clonk.c4group","c4f":"application/vnd.clonk.c4group","c4g":"application/vnd.clonk.c4group","c4p":"application/vnd.clonk.c4group","c4u":"application/vnd.clonk.c4group","cab":"application/vnd.ms-cab-compressed","caf":"audio/x-caf","cap":"application/vnd.tcpdump.pcap","car":"application/vnd.curl.car","cat":"application/vnd.ms-pki.seccat","cb7":"application/x-cbr","cba":"application/x-cbr","cbr":"application/x-cbr","cbt":"application/x-cbr","cbz":"application/x-cbr","cc":"text/x-c","cco":"application/x-cocoa","cct":"application/x-director","ccxml":"application/ccxml+xml","cdbcmsg":"application/vnd.contact.cmsg","cdf":"application/x-netcdf","cdfx":"application/cdfx+xml","cdkey":"application/vnd.mediastation.cdkey","cdmia":"application/cdmi-capability","cdmic":"application/cdmi-container","cdmid":"application/cdmi-domain","cdmio":"application/cdmi-object","cdmiq":"application/cdmi-queue","cdx":"chemical/x-cdx","cdxml":"application/vnd.chemdraw+xml","cdy":"application/vnd.cinderella","cer":"application/pkix-cert","cfs":"application/x-cfs-compressed","cgm":"image/cgm","chat":"application/x-chat","chm":"application/vnd.ms-htmlhelp","chrt":"application/vnd.kde.kchart","cif":"chemical/x-cif","cii":"application/vnd.an
function responseHeaders(headers, httpCache = false) {
headers = { ...DEFAULT_HEADERS, ...headers };
if (httpCache) {
headers['cache-control'] = 'max-age=3600';
delete headers['date'];
delete headers['expires'];
}
return headers;
}
const DEFAULT_HEADERS = {
'cache-control': 'no-cache, no-store, must-revalidate, max-age=0',
expires: '0',
date: 'Wed, 1 Jan 2000 00:00:00 GMT',
server: 'Stencil Dev Server ' + version,
'access-control-allow-origin': '*',
'access-control-expose-headers': '*',
};
function getBrowserUrl(protocol, address, port, basePath, pathname) {
address = address === `0.0.0.0` ? `localhost` : address;
const portSuffix = !port || port === 80 || port === 443 ? '' : ':' + port;
let path = basePath;
if (pathname.startsWith('/')) {
pathname = pathname.substring(1);
}
path += pathname;
protocol = protocol.replace(/\:/g, '');
return `${protocol}://${address}${portSuffix}${path}`;
}
function getDevServerClientUrl(devServerConfig, host, protocol) {
let address = devServerConfig.address;
let port = devServerConfig.port;
if (host) {
address = host;
port = null;
}
return getBrowserUrl(protocol !== null && protocol !== void 0 ? protocol : devServerConfig.protocol, address, port, devServerConfig.basePath, DEV_SERVER_URL);
}
function getContentType(filePath) {
const last = filePath.replace(/^.*[/\\]/, '').toLowerCase();
const ext = last.replace(/^.*\./, '').toLowerCase();
const hasPath = last.length < filePath.length;
const hasDot = ext.length < last.length - 1;
return ((hasDot || !hasPath) && contentTypes[ext]) || 'application/octet-stream';
}
function isHtmlFile(filePath) {
filePath = filePath.toLowerCase().trim();
return filePath.endsWith('.html') || filePath.endsWith('.htm');
}
function isCssFile(filePath) {
filePath = filePath.toLowerCase().trim();
return filePath.endsWith('.css');
}
const TXT_EXT = ['css', 'html', 'htm', 'js', 'json', 'svg', 'xml'];
function isSimpleText(filePath) {
const ext = filePath.toLowerCase().trim().split('.').pop();
return TXT_EXT.includes(ext);
}
function isExtensionLessPath(pathname) {
const parts = pathname.split('/');
const lastPart = parts[parts.length - 1];
return !lastPart.includes('.');
}
function isSsrStaticDataPath(pathname) {
const parts = pathname.split('/');
const fileName = parts[parts.length - 1].split('?')[0];
return fileName === 'page.state.json';
}
function getSsrStaticDataPath(req) {
const parts = req.url.href.split('/');
const fileName = parts[parts.length - 1];
const fileNameParts = fileName.split('?');
parts.pop();
let ssrPath = new URL(parts.join('/')).href;
if (!ssrPath.endsWith('/') && req.headers) {
const h = new Headers(req.headers);
if (h.get('referer').endsWith('/')) {
ssrPath += '/';
}
}
return {
ssrPath,
fileName: fileNameParts[0],
hasQueryString: typeof fileNameParts[1] === 'string' && fileNameParts[1].length > 0,
};
}
function isDevClient(pathname) {
return pathname.startsWith(DEV_SERVER_URL);
}
function isDevModule(pathname) {
return pathname.includes(DEV_MODULE_URL);
}
function isOpenInEditor(pathname) {
return pathname === OPEN_IN_EDITOR_URL;
}
function isInitialDevServerLoad(pathname) {
return pathname === DEV_SERVER_INIT_URL;
}
function isDevServerClient(pathname) {
return pathname === DEV_SERVER_URL;
}
function shouldCompress(devServerConfig, req) {
if (!devServerConfig.gzip) {
return false;
}
if (req.method !== 'GET') {
return false;
}
const acceptEncoding = req.headers && req.headers['accept-encoding'];
if (typeof acceptEncoding !== 'string') {
return false;
}
if (!acceptEncoding.includes('gzip')) {
return false;
}
return true;
}
function createCommonjsModule(fn, basedir, module) {
return module = {
path: basedir,
exports: {},
require: function (path, base) {
return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
}
}, fn(module, module.exports), module.exports;
}
function commonjsRequire () {
throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
}
let isDocker;
function hasDockerEnv() {
try {
fs__default['default'].statSync('/.dockerenv');
return true;
} catch (_) {
return false;
}
}
function hasDockerCGroup() {
try {
return fs__default['default'].readFileSync('/proc/self/cgroup', 'utf8').includes('docker');
} catch (_) {
return false;
}
}
var isDocker_1 = () => {
if (isDocker === undefined) {
isDocker = hasDockerEnv() || hasDockerCGroup();
}
return isDocker;
};
var isWsl_1 = createCommonjsModule(function (module) {
const isWsl = () => {
if (process.platform !== 'linux') {
return false;
}
if (os__default['default'].release().toLowerCase().includes('microsoft')) {
if (isDocker_1()) {
return false;
}
return true;
}
try {
return fs__default['default'].readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft') ?
!isDocker_1() : false;
} catch (_) {
return false;
}
};
if (process.env.__IS_WSL_TEST__) {
module.exports = isWsl;
} else {
module.exports = isWsl();
}
});
var defineLazyProp = (object, propertyName, fn) => {
const define = value => Object.defineProperty(object, propertyName, {value, enumerable: true, writable: true});
Object.defineProperty(object, propertyName, {
configurable: true,
enumerable: true,
get() {
const result = fn();
define(result);
return result;
},
set(value) {
define(value);
}
});
return object;
};
const {promises: fs, constants: fsConstants} = fs__default['default'];
// Path to included `xdg-open`.
const localXdgOpenPath = path__default['default'].join(__dirname, 'xdg-open');
const {platform, arch} = process;
/**
Get the mount point for fixed drives in WSL.
@inner
@returns {string} The mount point.
*/
const getWslDrivesMountPoint = (() => {
// Default value for "root" param
// according to https://docs.microsoft.com/en-us/windows/wsl/wsl-config
const defaultMountPoint = '/mnt/';
let mountPoint;
return async function () {
if (mountPoint) {
// Return memoized mount point value
return mountPoint;
}
const configFilePath = '/etc/wsl.conf';
let isConfigFileExists = false;
try {
await fs.access(configFilePath, fsConstants.F_OK);
isConfigFileExists = true;
} catch {}
if (!isConfigFileExists) {
return defaultMountPoint;
}
const configContent = await fs.readFile(configFilePath, {encoding: 'utf8'});
const configMountPoint = /(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(configContent);
if (!configMountPoint) {
return defaultMountPoint;
}
mountPoint = configMountPoint.groups.mountPoint.trim();
mountPoint = mountPoint.endsWith('/') ? mountPoint : `${mountPoint}/`;
return mountPoint;
};
})();
const pTryEach = async (array, mapper) => {
let latestError;
for (const item of array) {
try {
return await mapper(item); // eslint-disable-line no-await-in-loop
} catch (error) {
latestError = error;
}
}
throw latestError;
};
const baseOpen = async options => {
options = {
wait: false,
background: false,
newInstance: false,
allowNonzeroExitCode: false,
...options
};
if (Array.isArray(options.app)) {
return pTryEach(options.app, singleApp => baseOpen({
...options,
app: singleApp
}));
}
let {name: app, arguments: appArguments = []} = options.app || {};
appArguments = [...appArguments];
if (Array.isArray(app)) {
return pTryEach(app, appName => baseOpen({
...options,
app: {
name: appName,
arguments: appArguments
}
}));
}
let command;
const cliArguments = [];
const childProcessOptions = {};
if (platform === 'darwin') {
command = 'open';
if (options.wait) {
cliArguments.push('--wait-apps');
}
if (options.background) {
cliArguments.push('--background');
}
if (options.newInstance) {
cliArguments.push('--new');
}
if (app) {
cliArguments.push('-a', app);
}
} else if (platform === 'win32' || (isWsl_1 && !isDocker_1())) {
const mountPoint = await getWslDrivesMountPoint();
command = isWsl_1 ?
`${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe` :
`${process.env.SYSTEMROOT}\\System32\\WindowsPowerShell\\v1.0\\powershell`;
cliArguments.push(
'-NoProfile',
'-NonInteractive',
'ExecutionPolicy',
'Bypass',
'-EncodedCommand'
);
if (!isWsl_1) {
childProcessOptions.windowsVerbatimArguments = true;
}
const encodedArguments = ['Start'];
if (options.wait) {
encodedArguments.push('-Wait');
}
if (app) {
// Double quote with double quotes to ensure the inner quotes are passed through.
// Inner quotes are delimited for PowerShell interpretation with backticks.
encodedArguments.push(`"\`"${app}\`""`, '-ArgumentList');
if (options.target) {
appArguments.unshift(options.target);
}
} else if (options.target) {
encodedArguments.push(`"${options.target}"`);
}
if (appArguments.length > 0) {
appArguments = appArguments.map(arg => `"\`"${arg}\`""`);
encodedArguments.push(appArguments.join(','));
}
// Using Base64-encoded command, accepted by PowerShell, to allow special characters.
options.target = Buffer.from(encodedArguments.join(' '), 'utf16le').toString('base64');
} else {
if (app) {
command = app;
} else {
// When bundled by Webpack, there's no actual package file path and no local `xdg-open`.
const isBundled = !__dirname || __dirname === '/';
// Check if local `xdg-open` exists and is executable.
let exeLocalXdgOpen = false;
try {
await fs.access(localXdgOpenPath, fsConstants.X_OK);
exeLocalXdgOpen = true;
} catch {}
const useSystemXdgOpen = process.versions.electron ||
platform === 'android' || isBundled || !exeLocalXdgOpen;
command = useSystemXdgOpen ? 'xdg-open' : localXdgOpenPath;
}
if (appArguments.length > 0) {
cliArguments.push(...appArguments);
}
if (!options.wait) {
// `xdg-open` will block the process unless stdio is ignored
// and it's detached from the parent even if it's unref'd.
childProcessOptions.stdio = 'ignore';
childProcessOptions.detached = true;
}
}
if (options.target) {
cliArguments.push(options.target);
}
if (platform === 'darwin' && appArguments.length > 0) {
cliArguments.push('--args', ...appArguments);
}
const subprocess = childProcess__default['default'].spawn(command, cliArguments, childProcessOptions);
if (options.wait) {
return new Promise((resolve, reject) => {
subprocess.once('error', reject);
subprocess.once('close', exitCode => {
if (options.allowNonzeroExitCode && exitCode > 0) {
reject(new Error(`Exited with code ${exitCode}`));
return;
}
resolve(subprocess);
});
});
}
subprocess.unref();
return subprocess;
};
const open = (target, options) => {
if (typeof target !== 'string') {
throw new TypeError('Expected a `target`');
}
return baseOpen({
...options,
target
});
};
const openApp = (name, options) => {
if (typeof name !== 'string') {
throw new TypeError('Expected a `name`');
}
const {arguments: appArguments = []} = options || {};
if (appArguments !== undefined && appArguments !== null && !Array.isArray(appArguments)) {
throw new TypeError('Expected `appArguments` as Array type');
}
return baseOpen({
...options,
app: {
name,
arguments: appArguments
}
});
};
function detectArchBinary(binary) {
if (typeof binary === 'string' || Array.isArray(binary)) {
return binary;
}
const {[arch]: archBinary} = binary;
if (!archBinary) {
throw new Error(`${arch} is not supported`);
}
return archBinary;
}
function detectPlatformBinary({[platform]: platformBinary}, {wsl}) {
if (wsl && isWsl_1) {
return detectArchBinary(wsl);
}
if (!platformBinary) {
throw new Error(`${platform} is not supported`);
}
return detectArchBinary(platformBinary);
}
const apps = {};
defineLazyProp(apps, 'chrome', () => detectPlatformBinary({
darwin: 'google chrome',
win32: 'chrome',
linux: ['google-chrome', 'google-chrome-stable', 'chromium']
}, {
wsl: {
ia32: '/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe',
x64: ['/mnt/c/Program Files/Google/Chrome/Application/chrome.exe', '/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe']
}
}));
defineLazyProp(apps, 'firefox', () => detectPlatformBinary({
darwin: 'firefox',
win32: 'C:\\Program Files\\Mozilla Firefox\\firefox.exe',
linux: 'firefox'
}, {
wsl: '/mnt/c/Program Files/Mozilla Firefox/firefox.exe'
}));
defineLazyProp(apps, 'edge', () => detectPlatformBinary({
darwin: 'microsoft edge',
win32: 'msedge',
linux: ['microsoft-edge', 'microsoft-edge-dev']
}, {
wsl: '/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe'
}));
open.apps = apps;
open.openApp = openApp;
var open_1 = open;
async function openInBrowser(opts) {
// await open(opts.url, { app: ['google chrome', '--auto-open-devtools-for-tabs'] });
await open_1(opts.url);
}
function createServerContext(sys, sendMsg, devServerConfig, buildResultsResolves, compilerRequestResolves) {
const logRequest = (req, status) => {
if (devServerConfig) {
sendMsg({
requestLog: {
method: req.method || '?',
url: req.pathname || '?',
status,
},
});
}
};
const serve500 = (req, res, error, xSource) => {
try {
res.writeHead(500, responseHeaders({
'content-type': 'text/plain; charset=utf-8',
'x-source': xSource,
}));
res.write(util__default['default'].inspect(error));
res.end();
logRequest(req, 500);
}
catch (e) {
sendMsg({ error: { message: 'serve500: ' + e } });
}
};
const serve404 = (req, res, xSource, content = null) => {
try {
if (req.pathname === '/favicon.ico') {
const defaultFavicon = path__default['default'].join(devServerConfig.devServerDir, 'static', 'favicon.ico');
res.writeHead(200, responseHeaders({
'content-type': 'image/x-icon',
'x-source': `favicon: ${xSource}`,
}));
const rs = fs__default$1['default'].createReadStream(defaultFavicon);
rs.on('error', (err) => {
res.writeHead(404, responseHeaders({
'content-type': 'text/plain; charset=utf-8',
'x-source': `createReadStream error: ${err}, ${xSource}`,
}));
res.write(util__default['default'].inspect(err));
res.end();
});
rs.pipe(res);
return;
}
if (content == null) {
content = ['404 File Not Found', 'Url: ' + req.pathname, 'File: ' + req.filePath].join('\n');
}
res.writeHead(404, responseHeaders({
'content-type': 'text/plain; charset=utf-8',
'x-source': xSource,
}));
res.write(content);
res.end();
logRequest(req, 400);
}
catch (e) {
serve500(req, res, e, xSource);
}
};
const serve302 = (req, res, pathname = null) => {
logRequest(req, 302);
res.writeHead(302, { location: pathname || devServerConfig.basePath || '/' });
res.end();
};
const getBuildResults = () => new Promise((resolve, reject) => {
if (serverCtx.isServerListening) {
buildResultsResolves.push({ resolve, reject });
sendMsg({ requestBuildResults: true });
}
else {
reject('dev server closed');
}
});
const getCompilerRequest = (compilerRequestPath) => new Promise((resolve, reject) => {
if (serverCtx.isServerListening) {
compilerRequestResolves.push({
path: compilerRequestPath,
resolve,
reject,
});
sendMsg({ compilerRequestPath });
}
else {
reject('dev server closed');
}
});
const serverCtx = {
connectorHtml: null,
dirTemplate: null,
getBuildResults,
getCompilerRequest,
isServerListening: false,
logRequest,
prerenderConfig: null,
serve302,
serve404,
serve500,
sys,
};
return serverCtx;
}
async function serveOpenInEditor(serverCtx, req, res) {
let status = 200;
const data = {};
try {
const editors = await getEditors();
if (editors.length > 0) {
await parseData(editors, serverCtx.sys, req, data);
await openDataInEditor(data);
}
else {
data.error = `no editors available`;
}
}
catch (e) {
data.error = e + '';
status = 500;
}
serverCtx.logRequest(req, status);
res.writeHead(status, responseHeaders({
'content-type': 'application/json; charset=utf-8',
}));
res.write(JSON.stringify(data, null, 2));
res.end();
}
async function parseData(editors, sys, req, data) {
const qs = req.searchParams;
if (!qs.has('file')) {
data.error = `missing file`;
return;
}
data.file = qs.get('file');
if (qs.has('line') && !isNaN(qs.get('line'))) {
data.line = parseInt(qs.get('line'), 10);
}
if (typeof data.line !== 'number' || data.line < 1) {
data.line = 1;
}
if (qs.has('column') && !isNaN(qs.get('column'))) {
data.column = parseInt(qs.get('column'), 10);
}
if (typeof data.column !== 'number' || data.column < 1) {
data.column = 1;
}
let editor = qs.get('editor');
if (typeof editor === 'string') {
editor = editor.trim().toLowerCase();
if (editors.some((e) => e.id === editor)) {
data.editor = editor;
}
else {
data.error = `invalid editor: ${editor}`;
return;
}
}
else {
data.editor = editors[0].id;
}
const stat = await sys.stat(data.file);
data.exists = stat.isFile;
}
async function openDataInEditor(data) {
if (!data.exists || data.error) {
return;
}
try {
const opts = {
editor: data.editor,
};
const editor = openInEditorApi__default['default'].configure(opts, (err) => (data.error = err + ''));
if (data.error) {
return;
}
data.open = `${data.file}:${data.line}:${data.column}`;
await editor.open(data.open);
}
catch (e) {
data.error = e + '';
}
}
let editors = null;
function getEditors() {
if (!editors) {
editors = new Promise(async (resolve) => {
const editors = [];
try {
await Promise.all(Object.keys(openInEditorApi__default['default'].editors).map(async (editorId) => {
const isSupported = await isEditorSupported(editorId);
editors.push({
id: editorId,
priority: EDITOR_PRIORITY[editorId],
supported: isSupported,
});
}));
}
catch (e) { }
resolve(editors
.filter((e) => e.supported)
.sort((a, b) => {
if (a.priority < b.priority)
return -1;
if (a.priority > b.priority)
return 1;
return 0;
})
.map((e) => {
return {
id: e.id,
name: EDITORS[e.id],
};
}));
});
}
return editors;
}
async function isEditorSupported(editorId) {
let isSupported = false;
try {
await openInEditorApi__default['default'].editors[editorId].detect();
isSupported = true;
}
catch (e) { }
return isSupported;
}
const EDITORS = {
atom: 'Atom',
code: 'Code',
emacs: 'Emacs',
idea14ce: 'IDEA 14 Community Edition',
phpstorm: 'PhpStorm',
sublime: 'Sublime',
webstorm: 'WebStorm',
vim: 'Vim',
visualstudio: 'Visual Studio',
};
const EDITOR_PRIORITY = {
code: 1,
atom: 2,
sublime: 3,
visualstudio: 4,
idea14ce: 5,
webstorm: 6,
phpstorm: 7,
vim: 8,
emacs: 9,
};
async function serveFile(devServerConfig, serverCtx, req, res) {
try {
if (isSimpleText(req.filePath)) {
// easy text file, use the internal cache
let content = await serverCtx.sys.readFile(req.filePath, 'utf8');
if (devServerConfig.websocket && isHtmlFile(req.filePath) && !isDevServerClient(req.pathname)) {
// auto inject our dev server script
content = appendDevServerClientScript(devServerConfig, req, content);
}
else if (isCssFile(req.filePath)) {
content = updateStyleUrls(req.url, content);
}
if (shouldCompress(devServerConfig, req)) {
// let's gzip this well known web dev text file
res.writeHead(200, responseHeaders({
'content-type': getContentType(req.filePath) + '; charset=utf-8',
'content-encoding': 'gzip',
vary: 'Accept-Encoding',
}));
zlib__namespace.gzip(content, { level: 9 }, (_, data) => {
res.end(data);
});
}
else {
// let's not gzip this file
res.writeHead(200, responseHeaders({
'content-type': getContentType(req.filePath) + '; charset=utf-8',
'content-length': buffer.Buffer.byteLength(content, 'utf8'),
}));
res.write(content);
res.end();
}
}
else {
// non-well-known text file or other file, probably best we use a stream
// but don't bother trying to gzip this file for the dev server
res.writeHead(200, responseHeaders({
'content-type': getContentType(req.filePath),
'content-length': req.stats.size,
}));
fs__default$1['default'].createReadStream(req.filePath).pipe(res);
}
serverCtx.logRequest(req, 200);
}
catch (e) {
serverCtx.serve500(req, res, e, 'serveFile');
}
}
function updateStyleUrls(url, oldCss) {
const versionId = url.searchParams.get('s-hmr');
const hmrUrls = url.searchParams.get('s-hmr-urls');
if (versionId && hmrUrls) {
hmrUrls.split(',').forEach((hmrUrl) => {
urlVersionIds.set(hmrUrl, versionId);
});
}
const reg = /url\((['"]?)(.*)\1\)/gi;
let result;
let newCss = oldCss;
while ((result = reg.exec(oldCss)) !== null) {
const oldUrl = result[2];
const parsedUrl = new URL(oldUrl, url);
const fileName = path__default['default'].basename(parsedUrl.pathname);
const versionId = urlVersionIds.get(fileName);
if (!versionId) {
continue;
}
parsedUrl.searchParams.set('s-hmr', versionId);
newCss = newCss.replace(oldUrl, parsedUrl.pathname);
}
return newCss;
}
const urlVersionIds = new Map();
function appendDevServerClientScript(devServerConfig, req, content) {
var _a, _b, _c;
const devServerClientUrl = getDevServerClientUrl(devServerConfig, (_b = (_a = req.headers) === null || _a === void 0 ? void 0 : _a['x-forwarded-host']) !== null && _b !== void 0 ? _b : req.host, (_c = req.headers) === null || _c === void 0 ? void 0 : _c['x-forwarded-proto']);
const iframe = `<iframe title="Stencil Dev Server Connector ${version} &#9889;" src="${devServerClientUrl}" style="display:block;width:0;height:0;border:0;visibility:hidden" aria-hidden="true"></iframe>`;
return appendDevServerClientIframe(content, iframe);
}
function appendDevServerClientIframe(content, iframe) {
if (content.includes('</body>')) {
return content.replace('</body>', `${iframe}</body>`);
}
if (content.includes('</html>')) {
return content.replace('</html>', `${iframe}</html>`);
}
return `${content}${iframe}`;
}
async function serveDevClient(devServerConfig, serverCtx, req, res) {
try {
if (isOpenInEditor(req.pathname)) {
return serveOpenInEditor(serverCtx, req, res);
}
if (isDevServerClient(req.pathname)) {
return serveDevClientScript(devServerConfig, serverCtx, req, res);
}
if (isInitialDevServerLoad(req.pathname)) {
req.filePath = path__default['default'].join(devServerConfig.devServerDir, 'templates', 'initial-load.html');
}
else {
const staticFile = req.pathname.replace(DEV_SERVER_URL + '/', '');
req.filePath = path__default['default'].join(devServerConfig.devServerDir, 'static', staticFile);
}
try {
req.stats = await serverCtx.sys.stat(req.filePath);
if (req.stats.isFile) {
return serveFile(devServerConfig, serverCtx, req, res);
}
return serverCtx.serve404(req, res, 'serveDevClient not file');
}
catch (e) {
return serverCtx.serve404(req, res, `serveDevClient stats error ${e}`);
}
}
catch (e) {
return serverCtx.serve500(req, res, e, 'serveDevClient');
}
}
async function serveDevClientScript(devServerConfig, serverCtx, req, res) {
try {
if (serverCtx.connectorHtml == null) {
const filePath = path__default['default'].join(devServerConfig.devServerDir, 'connector.html');
serverCtx.connectorHtml = serverCtx.sys.readFileSync(filePath, 'utf8');
if (typeof serverCtx.connectorHtml !== 'string') {
return serverCtx.serve404(req, res, `serveDevClientScript`);
}
const devClientConfig = {
basePath: devServerConfig.basePath,
editors: await getEditors(),
reloadStrategy: devServerConfig.reloadStrategy,
};
serverCtx.connectorHtml = serverCtx.connectorHtml.replace('window.__DEV_CLIENT_CONFIG__', JSON.stringify(devClientConfig));
}
res.writeHead(200, responseHeaders({
'content-type': 'text/html; charset=utf-8',
}));
res.write(serverCtx.connectorHtml);
res.end();
}
catch (e) {
return serverCtx.serve500(req, res, e, `serveDevClientScript`);
}
}
async function serveDevNodeModule(serverCtx, req, res) {
try {
const results = await serverCtx.getCompilerRequest(req.pathname);
const headers = {
'content-type': 'application/javascript; charset=utf-8',
'content-length': Buffer.byteLength(results.content, 'utf8'),
'x-dev-node-module-id': results.nodeModuleId,
'x-dev-node-module-version': results.nodeModuleVersion,
'x-dev-node-module-resolved-path': results.nodeResolvedPath,
'x-dev-node-module-cache-path': results.cachePath,
'x-dev-node-module-cache-hit': results.cacheHit,
};
res.writeHead(results.status, responseHeaders(headers));
res.write(results.content);
res.end();
}
catch (e) {
serverCtx.serve500(req, res, e, `serveDevNodeModule`);
}
}
async function serveDirectoryIndex(devServerConfig, serverCtx, req, res) {
const indexFilePath = path__default['default'].join(req.filePath, 'index.html');
req.stats = await serverCtx.sys.stat(indexFilePath);
if (req.stats.isFile) {
req.filePath = indexFilePath;
return serveFile(devServerConfig, serverCtx, req, res);
}
if (!req.pathname.endsWith('/')) {
return serverCtx.serve302(req, res, req.pathname + '/');
}
try {
const dirFilePaths = await serverCtx.sys.readDir(req.filePath);
try {
if (serverCtx.dirTemplate == null) {
const dirTemplatePath = path__default['default'].join(devServerConfig.devServerDir, 'templates', 'directory-index.html');
serverCtx.dirTemplate = serverCtx.sys.readFileSync(dirTemplatePath);
}
const files = await getFiles(serverCtx.sys, req.url, dirFilePaths);
const templateHtml = serverCtx.dirTemplate
.replace('{{title}}', getTitle(req.pathname))
.replace('{{nav}}', getName(req.pathname))
.replace('{{files}}', files);
serverCtx.logRequest(req, 200);
res.writeHead(200, responseHeaders({
'content-type': 'text/html; charset=utf-8',
'x-directory-index': req.pathname,
}));
res.write(templateHtml);
res.end();
}
catch (e) {
return serverCtx.serve500(req, res, e, 'serveDirectoryIndex');
}
}
catch (e) {
return serverCtx.serve404(req, res, 'serveDirectoryIndex');
}
}
async function getFiles(sys, baseUrl, dirItemNames) {
const items = await getDirectoryItems(sys, baseUrl, dirItemNames);
if (baseUrl.pathname !== '/') {
items.unshift({
isDirectory: true,
pathname: '../',
name: '..',
});
}
return items
.map((item) => {
return `
<li class="${item.isDirectory ? 'directory' : 'file'}">
<a href="${item.pathname}">
<span class="icon"></span>
<span>${item.name}</span>
</a>
</li>`;
})
.join('');
}
async function getDirectoryItems(sys, baseUrl, dirFilePaths) {
const items = await Promise.all(dirFilePaths.map(async (dirFilePath) => {
const fileName = path__default['default'].basename(dirFilePath);
const url = new URL(fileName, baseUrl);
const stats = await sys.stat(dirFilePath);
const item = {
name: fileName,
pathname: url.pathname,
isDirectory: stats.isDirectory,
};
return item;
}));
return items;
}
function getTitle(pathName) {
return pathName;
}
function getName(pathName) {
const dirs = pathName.split('/');
dirs.pop();
let url = '';
return (dirs
.map((dir, index) => {
url += dir + '/';
const text = index === 0 ? `~` : dir;
return `<a href="${url}">${text}</a>`;
})
.join('<span>/</span>') + '<span>/</span>');
}
async function ssrPageRequest(devServerConfig, serverCtx, req, res) {
try {
let status = 500;
let content = '';
const { hydrateApp, srcIndexHtml, diagnostics } = await setupHydrateApp(devServerConfig, serverCtx);
if (!diagnostics.some((diagnostic) => diagnostic.level === 'error')) {
try {
const opts = getSsrHydrateOptions(devServerConfig, serverCtx, req.url);
const ssrResults = await hydrateApp.renderToString(srcIndexHtml, opts);
diagnostics.push(...ssrResults.diagnostics);
status = ssrResults.httpStatus;
content = ssrResults.html;
}
catch (e) {
catchError(diagnostics, e);
}
}
if (diagnostics.some((diagnostic) => diagnostic.level === 'error')) {
content = getSsrErrorContent(diagnostics);
status = 500;
}
if (devServerConfig.websocket) {
content = appendDevServerClientScript(devServerConfig, req, content);
}
serverCtx.logRequest(req, status);
res.writeHead(status, responseHeaders({
'content-type': 'text/html; charset=utf-8',
'content-length': Buffer.byteLength(content, 'utf8'),
}));
res.write(content);
res.end();
}
catch (e) {
serverCtx.serve500(req, res, e, `ssrPageRequest`);
}
}
async function ssrStaticDataRequest(devServerConfig, serverCtx, req, res) {
try {
const data = {};
let httpCache = false;
const { hydrateApp, srcIndexHtml, diagnostics } = await setupHydrateApp(devServerConfig, serverCtx);
if (!diagnostics.some((diagnostic) => diagnostic.level === 'error')) {
try {
const { ssrPath, hasQueryString } = getSsrStaticDataPath(req);
const url = new URL(ssrPath, req.url);
const opts = getSsrHydrateOptions(devServerConfig, serverCtx, url);
const ssrResults = await hydrateApp.renderToString(srcIndexHtml, opts);
diagnostics.push(...ssrResults.diagnostics);
ssrResults.staticData.forEach((s) => {
if (s.type === 'application/json') {
data[s.id] = JSON.parse(s.content);
}
else {
data[s.id] = s.content;
}
});
data.components = ssrResults.components.map((c) => c.tag).sort();
httpCache = hasQueryString;
}
catch (e) {
catchError(diagnostics, e);
}
}
if (diagnostics.length > 0) {
data.diagnostics = diagnostics;
}
const status = diagnostics.some((diagnostic) => diagnostic.level === 'error') ? 500 : 200;
const content = JSON.stringify(data);
serverCtx.logRequest(req, status);
res.writeHead(status, responseHeaders({
'content-type': 'application/json; charset=utf-8',
'content-length': Buffer.byteLength(content, 'utf8'),
}, httpCache && status === 200));
res.write(content);
res.end();
}
catch (e) {
serverCtx.serve500(req, res, e, `ssrStaticDataRequest`);
}
}
async function setupHydrateApp(devServerConfig, serverCtx) {
let srcIndexHtml = null;
let hydrateApp = null;
const buildResults = await serverCtx.getBuildResults();
const diagnostics = [];
if (serverCtx.prerenderConfig == null && isString(devServerConfig.prerenderConfig)) {
const compilerPath = path__default['default'].join(devServerConfig.devServerDir, '..', 'compiler', 'stencil.js');
const compiler = require(compilerPath);
const prerenderConfigResults = compiler.nodeRequire(devServerConfig.prerenderConfig);
diagnostics.push(...prerenderConfigResults.diagnostics);
if (prerenderConfigResults.module && prerenderConfigResults.module.config) {
serverCtx.prerenderConfig = prerenderConfigResults.module.config;
}
}
if (!isString(buildResults.hydrateAppFilePath)) {
diagnostics.push({ messageText: `Missing hydrateAppFilePath`, level: `error`, type: `ssr` });
}
else if (!isString(devServerConfig.srcIndexHtml)) {
diagnostics.push({ messageText: `Missing srcIndexHtml`, level: `error`, type: `ssr` });
}
else {
srcIndexHtml = await serverCtx.sys.readFile(devServerConfig.srcIndexHtml);
if (!isString(srcIndexHtml)) {
diagnostics.push({
messageText: `Unable to load src index html: ${devServerConfig.srcIndexHtml}`,
level: `error`,
type: `ssr`,
});
}
else {
// ensure we cleared out node's internal require() cache for this file
const hydrateAppFilePath = path__default['default'].resolve(buildResults.hydrateAppFilePath);
// brute force way of clearning node's module cache
// not using `delete require.cache[id]` since it'll cause memory leaks
require.cache = {};
const Module = require('module');
Module._cache[hydrateAppFilePath] = undefined;
hydrateApp = require(hydrateAppFilePath);
}
}
return {
hydrateApp,
srcIndexHtml,
diagnostics,
};
}
function getSsrHydrateOptions(devServerConfig, serverCtx, url) {
const opts = {
url: url.href,
addModulePreloads: false,
approximateLineWidth: 120,
inlineExternalStyleSheets: false,
minifyScriptElements: false,
minifyStyleElements: false,
removeAttributeQuotes: false,
removeBooleanAttributeQuotes: false,
removeEmptyAttributes: false,
removeHtmlComments: false,
prettyHtml: true,
};
const prerenderConfig = serverCtx === null || serverCtx === void 0 ? void 0 : serverCtx.prerenderConfig;
if (isFunction(prerenderConfig === null || prerenderConfig === void 0 ? void 0 : prerenderConfig.hydrateOptions)) {
const userOpts = prerenderConfig.hydrateOptions(url);
if (userOpts) {
Object.assign(opts, userOpts);
}
}
if (isFunction(serverCtx.sys.applyPrerenderGlobalPatch)) {
const orgBeforeHydrate = opts.beforeHydrate;
opts.beforeHydrate = (document) => {
// patch this new window with the fetch global from node-fetch
const devServerBaseUrl = new URL(devServerConfig.browserUrl);
const devServerHostUrl = devServerBaseUrl.origin;
serverCtx.sys.applyPrerenderGlobalPatch({
devServerHostUrl: devServerHostUrl,
window: document.defaultView,
});
if (typeof orgBeforeHydrate === 'function') {
return orgBeforeHydrate(document);
}
};
}
return opts;
}
function getSsrErrorContent(diagnostics) {
return `<!doctype html>
<html>
<head>
<title>SSR Error</title>
<style>
body {
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace !important;
}
</style>
</head>
<body>
<h1>SSR Dev Error</h1>
${diagnostics.map((diagnostic) => `
<p>
${diagnostic.messageText}
</p>
`)}
</body>
</html>`;
}
function createRequestHandler(devServerConfig, serverCtx) {
let userRequestHandler = null;
if (typeof devServerConfig.requestListenerPath === 'string') {
userRequestHandler = require(devServerConfig.requestListenerPath);
}
return async function (incomingReq, res) {
async function defaultHandler() {
try {
const req = normalizeHttpRequest(devServerConfig, incomingReq);
if (!req.url) {
return serverCtx.serve302(req, res);
}
if (isDevClient(req.pathname) && devServerConfig.websocket) {
return serveDevClient(devServerConfig, serverCtx, req, res);
}
if (isDevModule(req.pathname)) {
return serveDevNodeModule(serverCtx, req, res);
}
if (!isValidUrlBasePath(devServerConfig.basePath, req.url)) {
return serverCtx.serve404(req, res, `invalid basePath`, `404 File Not Found, base path: ${devServerConfig.basePath}`);
}
if (devServerConfig.ssr) {
if (isExtensionLessPath(req.url.pathname)) {
return ssrPageRequest(devServerConfig, serverCtx, req, res);
}
if (isSsrStaticDataPath(req.url.pathname)) {
return ssrStaticDataRequest(devServerConfig, serverCtx, req, res);
}
}
req.stats = await serverCtx.sys.stat(req.filePath);
if (req.stats.isFile) {
return serveFile(devServerConfig, serverCtx, req, res);
}
if (req.stats.isDirectory) {
return serveDirectoryIndex(devServerConfig, serverCtx, req, res);
}
const xSource = ['notfound'];
const validHistoryApi = isValidHistoryApi(devServerConfig, req);
xSource.push(`validHistoryApi: ${validHistoryApi}`);
if (validHistoryApi) {
try {
const indexFilePath = path__default['default'].join(devServerConfig.root, devServerConfig.historyApiFallback.index);
xSource.push(`indexFilePath: ${indexFilePath}`);
req.stats = await serverCtx.sys.stat(indexFilePath);
if (req.stats.isFile) {
req.filePath = indexFilePath;
return serveFile(devServerConfig, serverCtx, req, res);
}
}
catch (e) {
xSource.push(`notfound error: ${e}`);
}
}
return serverCtx.serve404(req, res, xSource.join(', '));
}
catch (e) {
return serverCtx.serve500(incomingReq, res, e, `not found error`);
}
}
if (typeof userRequestHandler === 'function') {
await userRequestHandler(incomingReq, res, defaultHandler);
}
else {
await defaultHandler();
}
};
}
function isValidUrlBasePath(basePath, url) {
// normalize the paths to always end with a slash for the check
let pathname = url.pathname;
if (!pathname.endsWith('/')) {
pathname += '/';
}
if (!basePath.endsWith('/')) {
basePath += '/';
}
return pathname.startsWith(basePath);
}
function normalizeHttpRequest(devServerConfig, incomingReq) {
const req = {
method: (incomingReq.method || 'GET').toUpperCase(),
headers: incomingReq.headers,
acceptHeader: (incomingReq.headers && typeof incomingReq.headers.accept === 'string' && incomingReq.headers.accept) || '',
host: (incomingReq.headers && typeof incomingReq.headers.host === 'string' && incomingReq.headers.host) || null,
url: null,
searchParams: null,
};
const incomingUrl = (incomingReq.url || '').trim() || null;
if (incomingUrl) {
if (req.host) {
req.url = new URL(incomingReq.url, `http://${req.host}`);
}
else {
req.url = new URL(incomingReq.url, `http://dev.stenciljs.com`);
}
req.searchParams = req.url.searchParams;
}
if (req.url) {
const parts = req.url.pathname.replace(/\\/g, '/').split('/');
req.pathname = parts.map((part) => decodeURIComponent(part)).join('/');
if (req.pathname.length > 0 && !isDevClient(req.pathname)) {
req.pathname = '/' + req.pathname.substring(devServerConfig.basePath.length);
}
req.filePath = normalizePath(path__default['default'].normalize(path__default['default'].join(devServerConfig.root, path__default['default'].relative('/', req.pathname))));
}
return req;
}
function isValidHistoryApi(devServerConfig, req) {
if (!devServerConfig.historyApiFallback) {
return false;
}
if (req.method !== 'GET') {
return false;
}
if (!req.acceptHeader.includes('text/html')) {
return false;
}
if (!devServerConfig.historyApiFallback.disableDotRule && req.pathname.includes('.')) {
return false;
}
return true;
}
function createHttpServer(devServerConfig, serverCtx) {
// create our request handler
const reqHandler = createRequestHandler(devServerConfig, serverCtx);
const credentials = devServerConfig.https;
return credentials ? https__namespace.createServer(credentials, reqHandler) : http__namespace.createServer(reqHandler);
}
async function findClosestOpenPort(host, port) {
async function t(portToCheck) {
const isTaken = await isPortTaken(host, portToCheck);
if (!isTaken) {
return portToCheck;
}
return t(portToCheck + 1);
}
return t(port);
}
function isPortTaken(host, port) {
return new Promise((resolve, reject) => {
const tester = net__namespace
.createServer()
.once('error', () => {
resolve(true);
})
.once('listening', () => {
tester
.once('close', () => {
resolve(false);
})
.close();
})
.on('error', (err) => {
reject(err);
})
.listen(port, host);
});
}
function createWebSocket(httpServer, onMessageFromClient) {
const wsConfig = {
server: httpServer,
};
const wsServer = new ws__namespace.Server(wsConfig);
function heartbeat() {
// we need to coerce the `ws` type to our custom `DevWS` type here, since
// this function is going to be passed in to `ws.on('pong'` which expects
// to be passed a functon where `this` is bound to `ws`.
this.isAlive = true;
}
wsServer.on('connection', (ws) => {
ws.on('message', (data) => {
// the server process has received a message from the browser
// pass the message received from the browser to the main cli process
try {
onMessageFromClient(JSON.parse(data.toString()));
}
catch (e) {
console.error(e);
}
});
ws.isAlive = true;
ws.on('pong', heartbeat);
// ignore invalid close frames sent by Safari 15
ws.on('error', console.error);
});
const pingInternval = setInterval(() => {
wsServer.clients.forEach((ws) => {
if (!ws.isAlive) {
return ws.close(1000);
}
ws.isAlive = false;
ws.ping(noop);
});
}, 10000);
return {
sendToBrowser: (msg) => {
if (msg && wsServer && wsServer.clients) {
const data = JSON.stringify(msg);
wsServer.clients.forEach((ws) => {
if (ws.readyState === ws.OPEN) {
ws.send(data);
}
});
}
},
close: () => {
return new Promise((resolve, reject) => {
clearInterval(pingInternval);
wsServer.clients.forEach((ws) => {
ws.close(1000);
});
wsServer.close((err) => {
if (err) {
reject(err);
}
else {
resolve();
}
});
});
},
};
}
function initServerProcess(sendMsg) {
let server = null;
let webSocket = null;
let serverCtx = null;
const buildResultsResolves = [];
const compilerRequestResolves = [];
const startServer = async (msg) => {
const devServerConfig = msg.startServer;
devServerConfig.port = await findClosestOpenPort(devServerConfig.address, devServerConfig.port);
devServerConfig.browserUrl = getBrowserUrl(devServerConfig.protocol, devServerConfig.address, devServerConfig.port, devServerConfig.basePath, '/');
devServerConfig.root = normalizePath(devServerConfig.root);
const sys = index_js.createNodeSys({ process });
serverCtx = createServerContext(sys, sendMsg, devServerConfig, buildResultsResolves, compilerRequestResolves);
server = createHttpServer(devServerConfig, serverCtx);
webSocket = devServerConfig.websocket ? createWebSocket(server, sendMsg) : null;
server.listen(devServerConfig.port, devServerConfig.address);
serverCtx.isServerListening = true;
if (devServerConfig.openBrowser) {
const initialLoadUrl = getBrowserUrl(devServerConfig.protocol, devServerConfig.address, devServerConfig.port, devServerConfig.basePath, devServerConfig.initialLoadUrl || DEV_SERVER_INIT_URL);
openInBrowser({ url: initialLoadUrl });
}
sendMsg({ serverStarted: devServerConfig });
};
const closeServer = () => {
const promises = [];
buildResultsResolves.forEach((r) => r.reject('dev server closed'));
buildResultsResolves.length = 0;
compilerRequestResolves.forEach((r) => r.reject('dev server closed'));
compilerRequestResolves.length = 0;
if (serverCtx) {
if (serverCtx.sys) {
promises.push(serverCtx.sys.destroy());
}
}
if (webSocket) {
promises.push(webSocket.close());
webSocket = null;
}
if (server) {
promises.push(new Promise((resolve) => {
server.close((err) => {
if (err) {
console.error(`close error: ${err}`);
}
resolve();
});
}));
}
Promise.all(promises).finally(() => {
sendMsg({
serverClosed: true,
});
});
};
const receiveMessageFromMain = (msg) => {
// the server process received a message from main thread
try {
if (msg) {
if (msg.startServer) {
startServer(msg);
}
else if (msg.closeServer) {
closeServer();
}
else if (msg.compilerRequestResults) {
for (let i = compilerRequestResolves.length - 1; i >= 0; i--) {
const r = compilerRequestResolves[i];
if (r.path === msg.compilerRequestResults.path) {
r.resolve(msg.compilerRequestResults);
compilerRequestResolves.splice(i, 1);
}
}
}
else if (serverCtx) {
if (msg.buildResults && !msg.isActivelyBuilding) {
buildResultsResolves.forEach((r) => r.resolve(msg.buildResults));
buildResultsResolves.length = 0;
}
if (webSocket) {
webSocket.sendToBrowser(msg);
}
}
}
}
catch (e) {
let stack = null;
if (e instanceof Error) {
stack = e.stack;
}
sendMsg({
error: { message: e + '', stack },
});
}
};
return receiveMessageFromMain;
}
exports.initServerProcess = initServerProcess;