Skip to content

Commit

Permalink
[gem-book] Support esm theme, config
Browse files Browse the repository at this point in the history
  • Loading branch information
mantou132 committed Jan 2, 2024
1 parent 13b3501 commit 4d9152a
Show file tree
Hide file tree
Showing 12 changed files with 90 additions and 67 deletions.
5 changes: 3 additions & 2 deletions packages/gem-book/docs/zh/002-guide/003-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ npx gem-book -h

## 配置文件

`gem-book` 命令会自动从项目根目录查找配置文件 `gem-book.cli.json`,支持大部分命令行选项(同时提供时合并命令行选项),例如:
`gem-book` 命令会自动从项目根目录查找配置文件 `gem-book.cli.{js|json|mjs}`,支持大部分命令行选项(同时提供时合并命令行选项),例如:

<gbp-raw src="gem-book.cli.json" range="1,3-"><gbp-raw>

> [!TIP]
> 使用 json schema: `"$schema": "https://unpkg.com/gem-book/schema.json"`
> 如果用 `json` 格式,可以添加 `"$schema": "https://unpkg.com/gem-book/schema.json"` 以获取类型提示,
> 如果使用 `js` 格式,可以导入类型 `import('gem-book/common/config').CliConfig`
## 命令行选项

Expand Down
4 changes: 2 additions & 2 deletions packages/gem-book/docs/zh/002-guide/005-theme.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@

## 自定义主题

可以直接使用命令行参数提供 `json`/`CommonJs` 格式的主题文件路径或者[内置主题](https://github.com/mantou132/gem/tree/master/packages/gem-book/themes)名称:
可以直接使用命令行参数提供 `json`/`CommonJs/ESM` 格式的主题文件路径或者[内置主题](https://github.com/mantou132/gem/tree/master/packages/gem-book/themes)名称:

```bash
gem-book docs --theme path/my-theme
gem-book docs --theme path/my-theme.mjs
gem-book docs --theme dark
```

Expand Down
6 changes: 3 additions & 3 deletions packages/gem-book/src/bin/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ import { GenerateSW } from 'workbox-webpack-plugin';
import { BookConfig, CliUniqueConfig } from '../common/config';
import { STATS_FILE } from '../common/constant';

import { resolveLocalPlugin, resolveTheme, isURL, requireObject } from './utils';
import { resolveLocalPlugin, resolveTheme, isURL, importObject } from './utils';

const publicDir = path.resolve(__dirname, '../public');
const entryDir = path.resolve(__dirname, process.env.GEM_BOOK_DEV ? '../src/website' : '../website');
const pluginDir = path.resolve(__dirname, process.env.GEM_BOOK_DEV ? '../src/plugins' : '../plugins');

// dev mode uses memory file system
export function startBuilder(dir: string, options: Required<CliUniqueConfig>, bookConfig: Partial<BookConfig>) {
export async function startBuilder(dir: string, options: Required<CliUniqueConfig>, bookConfig: BookConfig) {
const { debug, build, theme, template, output, icon, plugin, ga } = options;

const plugins = [...plugin];
Expand Down Expand Up @@ -89,7 +89,7 @@ export function startBuilder(dir: string, options: Required<CliUniqueConfig>, bo
new webpack.DefinePlugin({
'process.env.DEV_MODE': !build,
'process.env.BOOK_CONFIG': JSON.stringify(bookConfig),
'process.env.THEME': JSON.stringify(requireObject(themePath)),
'process.env.THEME': JSON.stringify(await importObject(themePath)),
'process.env.PLUGINS': JSON.stringify(plugins),
'process.env.GA_ID': JSON.stringify(ga),
}),
Expand Down
57 changes: 31 additions & 26 deletions packages/gem-book/src/bin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,13 @@ import {
getHash,
getFile,
resolveTheme,
requireObject,
importObject,
resolveModule,
} from './utils';
import { startBuilder } from './builder';
import lang from './lang.json'; // https://developers.google.com/search/docs/advanced/crawling/localized-versions#language-codes

export const devServerEventTarget = new EventTarget();
const devServerEventTarget = new EventTarget();

program.version(version, '-v, --version');

Expand All @@ -60,16 +61,16 @@ let cliConfig: Required<CliUniqueConfig> = {
config: '',
};

function readConfig(fullPath: string) {
const obj = requireObject<CliConfig & BookConfig>(fullPath) || {};
// 将配置文件和 cli 选项合并,并将 book 选项同步到 bookConfig
async function syncConfig(fullPath?: string) {
const obj: CliConfig = (await importObject(resolveModule(fullPath))) || {};
Object.keys(cliConfig).forEach((key: keyof CliUniqueConfig) => {
if (key in obj) {
const value = obj[key];
delete obj[key];

const cliConfigValue = cliConfig[key];

// Overriding command line options is not allowed
if (Array.isArray(cliConfigValue)) {
Object.assign(cliConfig, { [key]: [...new Set([...cliConfigValue, ...(value as any[])])] });
} else if (!cliConfigValue) {
Expand Down Expand Up @@ -233,6 +234,8 @@ async function generateBookConfig(dir: string) {
fs.writeFileSync(configPath, configStr);
}
}

if (cliConfig.debug) inspectObject(bookConfig, 2);
// eslint-disable-next-line no-console
console.log(`${new Date().toISOString()} book config updated! ${Date.now() - t}ms`);
}
Expand Down Expand Up @@ -316,18 +319,23 @@ program
.option('--debug', 'enabled debug mode', () => {
cliConfig.debug = true;
})
.option('--config <path>', `specify config file, default use \`${DEFAULT_CLI_FILE}\``, (configPath: string) => {
cliConfig.config = configPath;
})
.option(
'--config <path>',
`specify config file, default use \`${DEFAULT_CLI_FILE}\`.{js|json|mjs}`,
(configPath: string) => {
cliConfig.config = configPath;
},
)
.arguments('<dir>')
.action(async (dir: string) => {
const initCliOptions = structuredClone(cliConfig);
const initBookConfig = structuredClone(bookConfig);

docsRootDir = path.resolve(process.cwd(), dir);

const configPath = path.resolve(process.cwd(), cliConfig.config || DEFAULT_CLI_FILE);
readConfig(configPath);
const configPath = resolveModule(cliConfig.config || DEFAULT_CLI_FILE);
await syncConfig(configPath);
await generateBookConfig(dir);

const updateBookConfig = throttle(
async () => {
Expand All @@ -348,10 +356,10 @@ program
return fs.watch(
themePath,
throttle(
() => {
async () => {
devServerEventTarget.dispatchEvent(
Object.assign(new Event(UPDATE_EVENT), {
detail: { theme: requireObject(themePath) },
detail: { theme: await importObject(themePath) },
}),
);
},
Expand All @@ -362,9 +370,7 @@ program
}
};

await generateBookConfig(dir);

let server = cliConfig.json ? undefined : startBuilder(dir, cliConfig, bookConfig);
let server = cliConfig.json ? undefined : await startBuilder(dir, cliConfig, bookConfig);

if (!cliConfig.build) {
devServerEventTarget.addEventListener(UPDATE_EVENT, ({ detail }: CustomEvent<string>) => {
Expand All @@ -379,18 +385,17 @@ program
async () => {
cliConfig = structuredClone(initCliOptions);
bookConfig = structuredClone(initBookConfig);
readConfig(configPath);
await syncConfig(configPath);
await generateBookConfig(dir);
server?.stopCallback(() => {
server = startBuilder(dir, cliConfig, bookConfig);
devServerEventTarget.dispatchEvent(
Object.assign(new Event(UPDATE_EVENT), {
detail: { config: bookConfig },
}),
);
themeWatcher?.close();
themeWatcher = watchTheme();
});
await server?.stop();
server = await startBuilder(dir, cliConfig, bookConfig);
devServerEventTarget.dispatchEvent(
Object.assign(new Event(UPDATE_EVENT), {
detail: { config: bookConfig },
}),
);
themeWatcher?.close();
themeWatcher = watchTheme();
},
300,
{ trailing: true },
Expand Down
47 changes: 27 additions & 20 deletions packages/gem-book/src/bin/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ export function getRepoTitle() {
}
}

// dir2 is in dir
export function inTheDir(dir: string, dir2: string) {
return !path.relative(dir, dir2).startsWith('.');
}

// Prefer built-in
export function resolveLocalPlugin(p: string) {
try {
Expand All @@ -65,23 +70,27 @@ export function resolveLocalPlugin(p: string) {
}
}

// Prefer built-in
export function resolveTheme(p: string) {
export function resolveModule(p?: string, builtInDirs: string[] = []) {
if (!p) return;

try {
return require.resolve(path.resolve(__dirname, `../themes/${p}`));
} catch {
try {
return require.resolve(path.resolve(process.cwd(), p));
} catch {}
}
return [...builtInDirs.map((dir) => path.resolve(__dirname, `${dir}${p}`)), path.resolve(process.cwd(), p)]
.map((p) => ['', '.js', '.json', '.mjs'].map((ext) => p + ext))
.flat()
.find((e) => existsSync(e));
}

export function requireObject<T>(fullPath?: string) {
// Prefer built-in
export function resolveTheme(p: string) {
return resolveModule(p, ['../themes/']);
}

export async function importObject<T>(fullPath?: string) {
if (!fullPath) return;
delete require.cache[fullPath];
try {
if (fullPath.endsWith('.mjs')) {
return (await import(`${fullPath}?v=${performance.now()}`)).default;
}
delete require.cache[fullPath];
return require(fullPath) as T;
} catch (err) {
// eslint-disable-next-line no-console
Expand Down Expand Up @@ -200,29 +209,27 @@ export function isURL(s: string) {
}
}

// dir2 is in dir
export function inTheDir(dir: string, dir2: string) {
return !path.relative(dir, dir2).startsWith('.');
}

export function isSomeContent(filePath: string, content: string) {
return existsSync(filePath) && content === readFileSync(filePath, 'utf-8');
}

export function inspectObject(obj: any) {
export function inspectObject(obj: any, depth = 0) {
// eslint-disable-next-line no-console
console.log(util.inspect(obj, { colors: true, depth: null }));
console.log(util.inspect(obj, { colors: true, depth: depth || null }));
}

export async function getIconDataUrl(filePath: string) {
if (isURL(filePath)) return filePath;
if (filePath.startsWith('data:')) return filePath;
if (filePath.endsWith('.svg')) {
return `data:image/svg+xml;base64,${btoa(readFileSync(path.resolve(process.cwd(), filePath), 'utf-8'))}`;
}
try {
const image = await Jimp.read(filePath);
return await image.clone().resize(Jimp.AUTO, 100).getBase64Async(Jimp.MIME_PNG);
} catch (err) {
if (isURL(filePath)) return filePath;
throw err;
// eslint-disable-next-line no-console
console.error(err);
return '';
}
}
2 changes: 1 addition & 1 deletion packages/gem-book/src/common/constant.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const UPDATE_EVENT = 'gen-book-update';

export const DEFAULT_CLI_FILE = 'gem-book.cli';
export const DEFAULT_FILE = 'gem-book.json';
export const DEFAULT_CLI_FILE = 'gem-book.cli.json';
export const STATS_FILE = 'stats.json';
export const DEFAULT_SOURCE_BRANCH = 'main';
4 changes: 2 additions & 2 deletions packages/gem-book/src/element/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ export class GemBookElement extends GemElement {
this.addEventListener('message', ({ detail }: CustomEvent) => {
const event = JSON.parse(detail);
if (typeof event.data !== 'object') return;
// eslint-disable-next-line no-console
if (this.dev) console.log('Event data', event.data);
const { filePath, content, config, theme, reload } = event.data;
if (event.type !== UPDATE_EVENT) return;
const routeELement = this.routeRef.element!;
Expand Down Expand Up @@ -323,5 +325,3 @@ export class GemBookElement extends GemElement {
);
}
}

export { GemBookPluginElement } from './elements/plugin';
4 changes: 4 additions & 0 deletions packages/gem-book/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export * from './element';

export * from './element/elements/plugin';

export * from './element/helper/default-theme';
9 changes: 9 additions & 0 deletions packages/gem-book/themes/cyberpunk.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @type {import('gem-book')['defaultTheme']}
*/
module.exports = {
backgroundColor: 'rgb(10, 23, 45)',
borderColor: 'rgb(19, 61, 201)',
textColor: 'rgb(11, 196, 207)',
primaryColor: 'rgb(240, 0, 219)',
};
6 changes: 0 additions & 6 deletions packages/gem-book/themes/cyberpunk.json

This file was deleted.

5 changes: 0 additions & 5 deletions packages/gem-book/themes/dark.json

This file was deleted.

8 changes: 8 additions & 0 deletions packages/gem-book/themes/dark.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* @type {import('gem-book')['defaultTheme']}
*/
export default {
backgroundColor: '#1a1a1a',
borderColor: 'rgb(102, 102, 102)',
textColor: 'rgb(240, 240, 240)',
};

0 comments on commit 4d9152a

Please sign in to comment.