@jakesidsmith/tsb
Version:
Dead simple TypeScript bundler, watcher, dev server, transpiler, and polyfiller
247 lines • 10.3 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTsbConfig = void 0;
const ts = __importStar(require("typescript"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const constants_1 = require("./constants");
const yup = __importStar(require("yup"));
const logger = __importStar(require("./logger"));
const semver = __importStar(require("semver"));
const utils_1 = require("./utils");
const CONFIG_VALIDATOR = yup
.object()
.shape({
// Required
main: yup.string().required(),
outDir: yup.string().required(),
// Base options
clearOutDirBefore: yup
.array()
.of(yup.mixed().oneOf(['build', 'watch', 'serve']))
.optional(),
mainOutSubDir: yup.string().optional(),
mainBundleName: yup.string().optional(),
tsconfigPath: yup.string().optional(),
indexHTMLPath: yup.string().optional(),
indexHTMLEnv: yup.object().optional(),
outputIndexHTMLFor: yup
.array()
.of(yup.mixed().oneOf(['build', 'watch', 'serve']))
.optional(),
insertScriptTag: yup
.mixed()
.oneOf(['body', 'head', false])
.optional(),
reactHotLoading: yup.boolean().optional(),
hashFilesFor: yup
.array()
.of(yup.mixed().oneOf(['build', 'watch', 'serve']))
.optional(),
additionalFilesToParse: yup.array().of(yup.string().required()).optional(),
env: yup.object().optional(),
// Dev server options
hotLoading: yup.boolean().optional(),
host: yup.string().optional(),
port: yup.number().optional(),
publicDir: yup.string().optional(),
publicPath: yup.string().optional(),
singlePageApp: yup.boolean().optional(),
headers: yup.lazy((value) => {
if (value) {
const keys = {};
Object.keys(value).forEach((key) => {
keys[key] = yup.string().required();
});
return yup.object().shape(keys).required();
}
return yup.mixed().optional();
}),
})
.required();
const getTsbConfig = (configPath) => {
var _a;
const fullConfigDir = path.dirname(configPath);
const tsconfigPath = path.join(process.cwd(), 'tsconfig.json');
const json = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
if (json.error) {
logger.error(`Error reading ${tsconfigPath}`);
logger.error(ts.flattenDiagnosticMessageText(json.error.messageText, '\n'));
process.exit(1);
}
const configFileContent = ts.parseJsonConfigFileContent(json.config, ts.sys, process.cwd());
if (configFileContent.errors.length) {
logger.error(`Error parsing ${tsconfigPath}`);
logger.error(configFileContent.errors
.map((diag) => ts.flattenDiagnosticMessageText(diag.messageText, '\n'))
.join('\n'));
process.exit(1);
}
const compilerOptions = Object.assign(Object.assign({}, configFileContent.options), { module: ts.ModuleKind.CommonJS });
const program = ts.createProgram({
rootNames: [configPath],
options: compilerOptions,
});
const sourceFile = program.getSourceFile(configPath);
if (!sourceFile) {
logger.error(`Could not get ${constants_1.PROGRAM} config source`);
return process.exit(1);
}
const preEmitDiagnostics = ts.getPreEmitDiagnostics(program, sourceFile);
if (preEmitDiagnostics === null || preEmitDiagnostics === void 0 ? void 0 : preEmitDiagnostics.length) {
logger.error(preEmitDiagnostics
.map((diag) => ts.flattenDiagnosticMessageText(diag.messageText, '\n'))
.join('\n'));
logger.error(`Invalid ${constants_1.CONFIG_FILE_NAME}`);
process.exit(1);
}
const transpileResult = ts.transpileModule(sourceFile.getText(), {
fileName: configPath,
compilerOptions,
});
if ((_a = transpileResult.diagnostics) === null || _a === void 0 ? void 0 : _a.length) {
logger.error(transpileResult.diagnostics
.map((diag) => ts.flattenDiagnosticMessageText(diag.messageText, '\n'))
.join('\n'));
logger.error(`Invalid ${constants_1.CONFIG_FILE_NAME}`);
process.exit(1);
}
const tsbConfigFunction = Function('exports', 'require', 'process', 'console', transpileResult.outputText);
const tsbConfigExports = {};
try {
tsbConfigFunction(tsbConfigExports, require, process, console);
}
catch (error) {
logger.error(error);
logger.error('Error running tsb.config.ts');
return process.exit(1);
}
if (!tsbConfigExports.default) {
logger.error('Your tsb.config.ts must export a default');
return process.exit(1);
}
try {
CONFIG_VALIDATOR.validateSync(tsbConfigExports.default);
}
catch (error) {
logger.error((0, utils_1.getErrorMessages)(error));
logger.error(`Invalid ${constants_1.CONFIG_FILE_NAME}`);
return process.exit(1);
}
const { default: config } = tsbConfigExports;
if (config.env) {
const missingEnvVars = Object.entries(config.env)
.map(([key, value]) => typeof value === 'undefined' && typeof process.env[key] === 'undefined'
? key
: null)
.filter((key) => key !== null);
if (missingEnvVars.length) {
missingEnvVars.forEach((envVar) => {
logger.error(`Could not get value for environment variable "${envVar}" defined in config.env`);
});
return process.exit(1);
}
}
let { indexHTMLEnv } = config;
if (indexHTMLEnv) {
const missingEnvVars = Object.entries(indexHTMLEnv)
.map(([key, value]) => typeof value === 'undefined' && typeof process.env[key] === 'undefined'
? key
: null)
.filter((key) => key !== null);
if (missingEnvVars.length) {
missingEnvVars.forEach((envVar) => {
logger.error(`Could not get value for environment variable "${envVar}" defined in config.indexHTMLEnv`);
});
return process.exit(1);
}
indexHTMLEnv = Object.entries(indexHTMLEnv).reduce((memo, [key]) => {
if (typeof process.env[key] !== 'undefined') {
return Object.assign(Object.assign({}, memo), { [key]: process.env[key] });
}
return memo;
}, indexHTMLEnv);
}
if (config.indexHTMLPath) {
const fullIndexHTMLPath = path.resolve(fullConfigDir, config.indexHTMLPath);
if (!fs.existsSync(fullIndexHTMLPath)) {
logger.error(`Could not find index file at "${fullIndexHTMLPath}"`);
return process.exit(1);
}
}
if (config.reactHotLoading) {
try {
require.resolve('react-hot-loader');
}
catch (reactHotLoaderError) {
logger.error(reactHotLoaderError);
logger.error('Could not resolve react-hot-loader (reactHotLoading is enabled)');
return process.exit(1);
}
const reactVersions = {
react: 'Not installed',
['react-dom']: 'Not installed',
['@hot-loader/react-dom']: 'Not installed',
};
Object.keys(reactVersions).forEach((lib) => {
try {
const packagePath = require.resolve(`${lib}/package.json`);
const packageContent = fs.readFileSync(packagePath, 'utf8');
let packageJson;
try {
packageJson = JSON.parse(packageContent);
}
catch (jsonError) {
logger.error(jsonError);
logger.error(`Failed to parse package.json of ${lib} (reactHotLoading is enabled)`);
process.exit(1);
}
if (!(packageJson === null || packageJson === void 0 ? void 0 : packageJson.version)) {
logger.error(`No version specified in package.json of ${lib} (reactHotLoading is enabled)`);
process.exit(1);
}
reactVersions[lib] = packageJson.version;
}
catch (error) {
logger.error(error);
logger.error(`Failed to check installed version of ${lib} (reactHotLoading is enabled)`);
process.exit(1);
}
});
const resolvedVersions = Object.values(reactVersions).map((version) => { var _a; return (_a = semver.coerce(version)) === null || _a === void 0 ? void 0 : _a.major; });
const allVersionsAreTheSame = resolvedVersions.every((version) => version === resolvedVersions[0]);
if (!allVersionsAreTheSame) {
logger.error('Versions of installed React dependencies required for hot loading did not match');
Object.keys(reactVersions).forEach((lib) => {
logger.info(` ${lib}: ${reactVersions[lib]}`);
});
return process.exit(1);
}
}
return Object.assign(Object.assign({}, config), { indexHTMLEnv });
};
exports.getTsbConfig = getTsbConfig;
//# sourceMappingURL=config.js.map