@nx/react
Version:
198 lines (197 loc) • 8.32 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupSsrGenerator = setupSsrGenerator;
const devkit_1 = require("@nx/devkit");
const versions_1 = require("../../utils/versions");
const ast_utils_1 = require("../../utils/ast-utils");
const ensure_typescript_1 = require("@nx/js/src/utils/typescript/ensure-typescript");
const path_1 = require("path");
let tsModule;
function readEntryFile(host, path) {
if (!tsModule) {
tsModule = (0, ensure_typescript_1.ensureTypescript)();
}
const content = host.read(path, 'utf-8');
return {
content,
source: tsModule.createSourceFile(path, content, tsModule.ScriptTarget.Latest, true),
};
}
async function getProjectConfig(tree, projectName) {
let maybeProjectConfig = (0, devkit_1.readProjectConfiguration)(tree, projectName);
if (!maybeProjectConfig.targets?.build) {
let projectGraph;
try {
projectGraph = (0, devkit_1.readCachedProjectGraph)();
}
catch {
projectGraph = await (0, devkit_1.createProjectGraphAsync)();
}
maybeProjectConfig = projectGraph.nodes[projectName].data;
}
return maybeProjectConfig;
}
async function setupSsrGenerator(tree, options) {
const projectConfig = await getProjectConfig(tree, options.project);
const projectRoot = projectConfig.root;
const appImportCandidates = [
options.appComponentImportPath ?? 'app/app',
'app',
'App',
'app/App',
'App/App',
].map((importPath) => {
return {
importPath,
filePath: (0, devkit_1.joinPathFragments)(projectConfig.sourceRoot || projectConfig.root, `${importPath}.tsx`),
};
});
const appComponentInfo = appImportCandidates.find((candidate) => tree.exists(candidate.filePath));
if (!appComponentInfo) {
throw new Error(`Cannot find an import path for <App/> component. Try passing setting --appComponentImportPath option.`);
}
if (!projectConfig.targets.build || !projectConfig.targets.serve) {
throw new Error(`Project ${options.project} does not have build and serve targets`);
}
if (projectConfig.targets.server) {
throw new Error(`Project ${options.project} already has a server target.`);
}
const originalOutputPath = projectConfig.targets.build?.options?.outputPath ??
projectConfig.targets.build?.outputs[0];
if (!originalOutputPath) {
throw new Error(`Project ${options.project} does not contain a outputPath for the build target.`);
}
// TODO(colum): We need to figure out how to handle this for Crystal
if (projectConfig.targets.build.options?.outputPath) {
projectConfig.targets.build.options.outputPath = (0, devkit_1.joinPathFragments)(originalOutputPath, 'browser');
}
if (projectConfig.targets.build.executor === '@nx/rspack:rspack') {
options.bundler = 'rspack';
}
else if (projectConfig.targets.build.executor === '@nx/webpack:webpack') {
options.bundler = 'webpack';
}
projectConfig.targets = {
...projectConfig.targets,
server: {
dependsOn: ['build'],
executor: options.bundler === 'rspack'
? '@nx/rspack:rspack'
: '@nx/webpack:webpack',
outputs: ['{options.outputPath}'],
defaultConfiguration: 'production',
options: {
target: 'node',
main: `${projectRoot}/server.ts`,
outputPath: (0, devkit_1.joinPathFragments)(originalOutputPath, 'server'),
outputFileName: 'server.js',
tsConfig: `${projectRoot}/tsconfig.server.json`,
compiler: 'babel',
externalDependencies: 'all',
outputHashing: 'none',
...(options.bundler === 'rspack'
? { rspackConfig: (0, devkit_1.joinPathFragments)(projectRoot, 'rspack.config.js') }
: {
webpackConfig: (0, devkit_1.joinPathFragments)(projectRoot, 'webpack.config.js'),
}),
},
configurations: {
development: {
optimization: false,
sourceMap: true,
},
production: {
fileReplacements: [
{
replace: `${projectRoot}/src/environments/environment.ts`,
with: `${projectRoot}/src/environments/environment.prod.ts`,
},
],
sourceMap: false,
},
},
},
'serve-browser': projectConfig.targets.serve,
'serve-server': {
executor: '@nx/js:node',
defaultConfiguration: 'development',
options: {
buildTarget: `${options.project}:server:development`,
buildTargetOptions: {
watch: true,
},
},
configurations: {
development: {},
production: {
buildTarget: `${options.project}:server:production`,
},
},
},
serve: {
executor: options.bundler === 'rspack'
? '@nx/rspack:ssr-dev-server'
: '@nx/webpack:ssr-dev-server',
defaultConfiguration: 'development',
options: {
browserTarget: `${options.project}:build:development`,
serverTarget: `${options.project}:serve-server:development`,
port: options.serverPort,
browserTargetOptions: {
watch: true,
},
},
configurations: {
development: {},
production: {
browserTarget: `${options.project}:build:production`,
serverTarget: `${options.project}:serve-server:production`,
},
},
},
};
(0, devkit_1.updateProjectConfiguration)(tree, options.project, projectConfig);
const nxJson = (0, devkit_1.readNxJson)(tree);
if (nxJson.tasksRunnerOptions?.default &&
!nxJson.tasksRunnerOptions?.default.options.cacheableOperations.includes('server')) {
nxJson.tasksRunnerOptions.default.options.cacheableOperations = [
...nxJson.tasksRunnerOptions.default.options.cacheableOperations,
'server',
];
}
nxJson.targetDefaults ??= {};
nxJson.targetDefaults['server'] ??= {};
nxJson.targetDefaults.server.cache = true;
(0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files'), projectRoot, {
tmpl: '',
port: Number(options?.serverPort) || 4200,
extraInclude: options.extraInclude?.length > 0
? `"${options.extraInclude.join('", "')}",`
: '',
appComponentImport: appComponentInfo.importPath,
browserBuildOutputPath: projectConfig.targets.build?.options?.outputPath ??
projectConfig.targets.build?.outputs[0],
});
// Add <StaticRouter> to server main if needed.
// TODO: need to read main.server.tsx not main.tsx.
const appContent = tree.read(appComponentInfo.filePath, 'utf-8');
const isRouterPresent = appContent.match(/react-router-dom/);
if (isRouterPresent) {
const serverEntry = (0, devkit_1.joinPathFragments)(projectRoot, 'src/main.server.tsx');
const { content, source } = readEntryFile(tree, serverEntry);
const changes = (0, devkit_1.applyChangesToString)(content, (0, ast_utils_1.addStaticRouter)(serverEntry, source));
tree.write(serverEntry, changes);
}
(0, devkit_1.updateNxJson)(tree, nxJson);
const installTask = (0, devkit_1.addDependenciesToPackageJson)(tree, {
express: versions_1.expressVersion,
isbot: versions_1.isbotVersion,
cors: versions_1.corsVersion,
}, {
'@types/express': versions_1.typesExpressVersion,
'@types/cors': versions_1.typesCorsVersion,
});
await (0, devkit_1.formatFiles)(tree);
return installTask;
}
exports.default = setupSsrGenerator;
;