Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions packages/cli/plugin-ssg/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@
"@modern-js/utils": "workspace:*",
"@swc/helpers": "^0.5.17",
"node-mocks-http": "^1.11.0",
"normalize-path": "3.0.0",
"portfinder": "^1.0.38"
"normalize-path": "3.0.0"
},
"peerDependencies": {
"react-router-dom": ">=7.0.0"
Expand Down
79 changes: 39 additions & 40 deletions packages/cli/plugin-ssg/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import path from 'path';
import type { AppTools, CliPlugin } from '@modern-js/app-tools';
import type { NestedRouteForCli, PageRoute } from '@modern-js/types';
import type {
NestedRouteForCli,
PageRoute,
SSGSingleEntryOptions,
} from '@modern-js/types';
import { filterRoutesForServer, logger } from '@modern-js/utils';
import { generatePath } from 'react-router-dom';
import { makeRoute } from './libs/make';
import { writeHtmlFile } from './libs/output';
import { replaceRoute } from './libs/replace';
Expand All @@ -15,7 +18,13 @@ import {
writeJSONSpec,
} from './libs/util';
import { createServer } from './server';
import type { AgreedRouteMap, SSGConfig, SsgRoute } from './types';
import type {
AgreedRoute,
AgreedRouteMap,
SSGConfig,
SSGRouteOptions,
SsgRoute,
} from './types';

export const ssgPlugin = (): CliPlugin<AppTools> => ({
name: '@modern-js/plugin-ssg',
Expand All @@ -42,11 +51,12 @@ export const ssgPlugin = (): CliPlugin<AppTools> => ({
const { output, server } = resolvedConfig;
const {
ssg,
ssgByEntries,
distPath: { root: outputPath } = {},
} = output;

const ssgOptions: SSGConfig =
(Array.isArray(ssg) ? ssg.pop() : ssg) || true;
(Array.isArray(ssg) ? ssg.pop() : ssg) ?? true;

const buildDir = path.join(appDirectory, outputPath as string);
const routes = readJSONSpec(buildDir);
Expand All @@ -65,6 +75,7 @@ export const ssgPlugin = (): CliPlugin<AppTools> => ({
entrypoints,
pageRoutes,
server,
ssgByEntries,
);

if (!intermediateOptions) {
Expand All @@ -76,9 +87,8 @@ export const ssgPlugin = (): CliPlugin<AppTools> => ({
pageRoutes.forEach(pageRoute => {
const { entryName, entryPath } = pageRoute;
const agreedRoutes = agreedRouteMap[entryName as string];
let entryOptions =
intermediateOptions[entryName as string] ||
intermediateOptions[pageRoute.urlPath];
let entryOptions = (intermediateOptions[entryName as string] ||
intermediateOptions[pageRoute.urlPath]) as SSGSingleEntryOptions;

if (!agreedRoutes) {
// default behavior for non-agreed route
Expand All @@ -93,7 +103,7 @@ export const ssgPlugin = (): CliPlugin<AppTools> => ({
// if entryOptions is object and has routes options
// add every route in options
const { routes: enrtyRoutes, headers } = entryOptions;
enrtyRoutes.forEach(route => {
enrtyRoutes.forEach((route: SSGRouteOptions) => {
ssgRoutes.push(makeRoute(pageRoute, route, headers));
});
}
Expand All @@ -105,42 +115,32 @@ export const ssgPlugin = (): CliPlugin<AppTools> => ({
}

if (entryOptions === true) {
entryOptions = { preventDefault: [], routes: [], headers: {} };
entryOptions = { routes: [], headers: {} };
}

const {
preventDefault = [],
routes: userRoutes = [],
headers,
} = entryOptions;
const { routes: userRoutes = [], headers } =
(entryOptions as {
routes?: SSGRouteOptions[];
headers?: Record<string, string>;
}) || {};
// if the user sets the routes, then only add them
if (userRoutes.length > 0) {
userRoutes.forEach(route => {
if (typeof route === 'string') {
ssgRoutes.push(makeRoute(pageRoute, route, headers));
} else if (Array.isArray(route.params)) {
route.params.forEach(param => {
ssgRoutes.push(
makeRoute(
pageRoute,
{ ...route, url: generatePath(route.url, param) },
headers,
),
);
});
} else {
ssgRoutes.push(makeRoute(pageRoute, route, headers));
(userRoutes as SSGRouteOptions[]).forEach(
(route: SSGRouteOptions) => {
if (typeof route === 'string') {
ssgRoutes.push(makeRoute(pageRoute, route, headers));
} else {
ssgRoutes.push(makeRoute(pageRoute, route, headers));
}
},
);
} else {
// default: add all non-dynamic routes
agreedRoutes.forEach((route: AgreedRoute) => {
if (!isDynamicUrl(route.path!)) {
ssgRoutes.push(makeRoute(pageRoute, route.path!, headers));
}
});
} else {
// otherwith add all except dynamic routes
agreedRoutes
.filter(route => !preventDefault.includes(route.path!))
.forEach(route => {
if (!isDynamicUrl(route.path!)) {
ssgRoutes.push(makeRoute(pageRoute, route.path!, headers));
}
});
}
}
});
Expand Down Expand Up @@ -177,12 +177,11 @@ export const ssgPlugin = (): CliPlugin<AppTools> => ({
});

const htmlAry = await createServer(
api,
appContext,
ssgRoutes,
pageRoutes,
apiRoutes,
resolvedConfig,
appDirectory,
);

// write to dist file
Expand Down
69 changes: 58 additions & 11 deletions packages/cli/plugin-ssg/src/libs/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function formatPath(str: string) {
}

export function isDynamicUrl(url: string): boolean {
return url.includes(':');
return url.includes(':') || url.endsWith('*');
}

export function getUrlPrefix(route: SsgRoute, baseUrl: string | string[]) {
Expand Down Expand Up @@ -112,7 +112,48 @@ export const standardOptions = (
entrypoints: EntryPoint[],
routes: ModernRoute[],
server: ServerUserConfig,
ssgByEntries?: SSGMultiEntryOptions,
) => {
if (ssgByEntries && Object.keys(ssgByEntries).length > 0) {
const result: SSGMultiEntryOptions = {};

Object.keys(ssgByEntries).forEach(key => {
const val = ssgByEntries[key];
if (typeof val !== 'function') {
result[key] = val;
}
});

for (const entry of entrypoints) {
const { entryName } = entry;
const configured = ssgByEntries[entryName];
if (typeof configured === 'function') {
const routesForEntry = routes.filter(r => r.entryName === entryName);
if (Array.isArray(server?.baseUrl)) {
for (const url of server.baseUrl) {
routesForEntry
.filter(
r => typeof r.urlPath === 'string' && r.urlPath.startsWith(url),
)
.forEach(r => {
result[r.urlPath as string] = configured(entryName, {
baseUrl: url,
});
});
}
} else {
result[entryName] = configured(entryName, {
baseUrl: server?.baseUrl,
});
}
} else if (typeof configured !== 'undefined') {
result[entryName] = configured;
}
}

return result;
}

if (ssgOptions === false) {
return false;
}
Expand All @@ -125,24 +166,30 @@ export const standardOptions = (
} else if (typeof ssgOptions === 'object') {
const isSingle = isSingleEntry(entrypoints);

if (isSingle && typeof (ssgOptions as any).main === 'undefined') {
if (isSingle) {
return { main: ssgOptions } as SSGMultiEntryOptions;
} else {
return ssgOptions as SSGMultiEntryOptions;
}

return entrypoints.reduce((opt, entry) => {
opt[entry.entryName] = ssgOptions;
return opt;
}, {} as SSGMultiEntryOptions);
} else if (typeof ssgOptions === 'function') {
const intermediateOptions: SSGMultiEntryOptions = {};
for (const entrypoint of entrypoints) {
const { entryName } = entrypoint;
// TODO: may be async function
const routesForEntry = routes.filter(r => r.entryName === entryName);
if (Array.isArray(server?.baseUrl)) {
for (const url of server.baseUrl) {
const matchUrl = entryName === 'main' ? url : `${url}/${entryName}`;
const route = routes.find(route => route.urlPath === matchUrl);
intermediateOptions[route?.urlPath as string] = ssgOptions(
entryName,
{ baseUrl: url },
);
routesForEntry
.filter(
r => typeof r.urlPath === 'string' && r.urlPath.startsWith(url),
)
.forEach(r => {
intermediateOptions[r.urlPath as string] = ssgOptions(entryName, {
baseUrl: url,
});
});
}
} else {
intermediateOptions[entryName] = ssgOptions(entryName, {
Expand Down
Loading
Loading