Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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