Skip to content

Commit

Permalink
feat(nodeRequire): export nodeRequire utility from compiler
Browse files Browse the repository at this point in the history
  • Loading branch information
adamdbradley committed Sep 16, 2020
1 parent 03decfa commit 10ea2fb
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 91 deletions.
38 changes: 6 additions & 32 deletions src/compiler/config/load-config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { CompilerSystem, Config, Diagnostic, LoadConfigInit, LoadConfigResults } from '../../declarations';
import { buildError, catchError, isString, normalizePath, hasError } from '@utils';
import { buildError, catchError, hasError, isString, normalizePath } from '@utils';
import { createLogger } from '../sys/logger/console-logger';
import { createSystem } from '../sys/stencil-sys';
import { dirname, resolve } from 'path';
import { dirname } from 'path';
import { IS_NODE_ENV } from '../sys/environment';
import { nodeRequire } from '../sys/node-require';
import { validateConfig } from './validate-config';
import { validateTsConfig } from '../sys/typescript/typescript-config';
import ts from 'typescript';
Expand Down Expand Up @@ -116,32 +117,9 @@ const evaluateConfigFile = async (sys: CompilerSystem, diagnostics: Diagnostic[]

try {
if (IS_NODE_ENV) {
// ensure we cleared out node's internal require() cache for this file
delete require.cache[resolve(configFilePath)];

// let's override node's require for a second
// don't worry, we'll revert this when we're done
require.extensions['.ts'] = (module: NodeModuleWithCompile, filename: string) => {
let sourceText = sys.readFileSync(filename, 'utf8');

if (configFilePath.endsWith('.ts')) {
// looks like we've got a typed config file
// let's transpile it to .js quick
sourceText = transpileTypedConfig(diagnostics, sourceText, configFilePath);
} else {
// quick hack to turn a modern es module
// into and old school commonjs module
sourceText = sourceText.replace(/export\s+\w+\s+(\w+)/gm, 'exports.$1');
}

module._compile(sourceText, filename);
};

// let's do this!
configFileData = require(configFilePath);

// all set, let's go ahead and reset the require back to the default
require.extensions['.ts'] = undefined;
const results = nodeRequire(configFilePath);
diagnostics.push(...results.diagnostics);
configFileData = results.module;
} else {
// browser environment, can't use node's require() to evaluate
let sourceText = await sys.readFile(configFilePath);
Expand Down Expand Up @@ -183,7 +161,3 @@ const transpileTypedConfig = (diagnostics: Diagnostic[], sourceText: string, fil

return output.outputText;
};

interface NodeModuleWithCompile extends NodeModule {
_compile(code: string, filename: string): any;
}
1 change: 1 addition & 0 deletions src/compiler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export { createWorkerContext } from './worker/worker-thread';
export { createWorkerMessageHandler } from './worker/worker-thread';
export { dependencies } from './sys/dependencies.json';
export { loadConfig } from './config/load-config';
export { nodeRequire } from './sys/node-require';
export { optimizeCss } from './optimize/optimize-css';
export { optimizeJs } from './optimize/optimize-js';
export { path } from './sys/modules/path';
Expand Down
120 changes: 62 additions & 58 deletions src/compiler/sys/modules/module.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,66 @@
/**
* Node builtin modules as of v14.5.0
*/
export const NODE_BUILTINS = [
'_http_agent',
'_http_client',
'_http_common',
'_http_incoming',
'_http_outgoing',
'_http_server',
'_stream_duplex',
'_stream_passthrough',
'_stream_readable',
'_stream_transform',
'_stream_wrap',
'_stream_writable',
'_tls_common',
'_tls_wrap',
'assert',
'async_hooks',
'buffer',
'child_process',
'cluster',
'console',
'constants',
'crypto',
'dgram',
'dns',
'domain',
'events',
'fs',
'fs/promises',
'http',
'http2',
'https',
'inspector',
'module',
'net',
'os',
'path',
'perf_hooks',
'process',
'punycode',
'querystring',
'readline',
'repl',
'stream',
'string_decoder',
'sys',
'timers',
'tls',
'trace_events',
'tty',
'url',
'util',
'v8',
'vm',
'worker_threads',
'zlib',
];

export default class Module {
static get builtinModules() {
// as of node v14.2.0
return [
'_http_agent',
'_http_client',
'_http_common',
'_http_incoming',
'_http_outgoing',
'_http_server',
'_stream_duplex',
'_stream_passthrough',
'_stream_readable',
'_stream_transform',
'_stream_wrap',
'_stream_writable',
'_tls_common',
'_tls_wrap',
'assert',
'async_hooks',
'buffer',
'child_process',
'cluster',
'console',
'constants',
'crypto',
'dgram',
'dns',
'domain',
'events',
'fs',
'fs/promises',
'http',
'http2',
'https',
'inspector',
'module',
'net',
'os',
'path',
'perf_hooks',
'process',
'punycode',
'querystring',
'readline',
'repl',
'stream',
'string_decoder',
'sys',
'timers',
'tls',
'trace_events',
'tty',
'url',
'util',
'v8',
'vm',
'worker_threads',
'zlib',
];
return NODE_BUILTINS;
}
}
72 changes: 72 additions & 0 deletions src/compiler/sys/node-require.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { Diagnostic } from '../../declarations';
import { IS_NODE_ENV } from './environment';
import { loadTypeScriptDiagnostic, catchError } from '@utils';
import ts from 'typescript';

export const nodeRequire = (id: string) => {
const results = {
module: undefined as any,
id,
diagnostics: [] as Diagnostic[],
};

if (IS_NODE_ENV) {
try {
const fs: typeof import('fs') = require('fs');
const path: typeof import('path') = require('path');

results.id = path.resolve(id);

// ensure we cleared out node's internal require() cache for this file
delete require.cache[results.id];

// let's override node's require for a second
// don't worry, we'll revert this when we're done
require.extensions['.ts'] = (module: NodeModuleWithCompile, fileName: string) => {
let sourceText = fs.readFileSync(fileName, 'utf8');

if (fileName.endsWith('.ts')) {
// looks like we've got a typed config file
// let's transpile it to .js quick
const tsResults = ts.transpileModule(sourceText, {
fileName,
compilerOptions: {
module: ts.ModuleKind.CommonJS,
moduleResolution: ts.ModuleResolutionKind.NodeJs,
esModuleInterop: true,
target: ts.ScriptTarget.ES2017,
allowJs: true,
},
});
sourceText = tsResults.outputText;

results.diagnostics.push(...tsResults.diagnostics.map(loadTypeScriptDiagnostic));
} else {
// quick hack to turn a modern es module
// into and old school commonjs module
sourceText = sourceText.replace(/export\s+\w+\s+(\w+)/gm, 'exports.$1');
}

try {
module._compile(sourceText, fileName);
} catch (e) {
catchError(results.diagnostics, e);
}
};

// let's do this!
results.module = require(results.id);

// all set, let's go ahead and reset the require back to the default
require.extensions['.ts'] = undefined;
} catch (e) {
catchError(results.diagnostics, e);
}
}

return results;
};

interface NodeModuleWithCompile extends NodeModule {
_compile(code: string, filename: string): any;
}
2 changes: 1 addition & 1 deletion src/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const pluck = (obj: { [key: string]: any }, keys: string[]) => {
export const isBoolean = (v: any): v is boolean => typeof v === 'boolean';
export const isDefined = (v: any) => v !== null && v !== undefined;
export const isUndefined = (v: any) => v === null || v === undefined;
export const isFunction = (v: any): v is boolean => typeof v === 'function';
export const isFunction = (v: any): v is Function => typeof v === 'function';
export const isNumber = (v: any): v is boolean => typeof v === 'number';
export const isObject = (val: Object) => val != null && typeof val === 'object' && Array.isArray(val) === false;
export const isString = (v: any): v is string => typeof v === 'string';
Expand Down

0 comments on commit 10ea2fb

Please sign in to comment.