文件
chen 301194963b
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
feat: Initial clean release of CHM
2026-11-04 11:29:39 +08:00

128 行
4.0 KiB
TypeScript

// React Native WebView cannot load external JS files, however it can load
// arbitrary JS via the injectedJavaScript property. So we use this to load external
// files: First here we convert the JS file to a plain string, and that string
// is then loaded by eg. the Mermaid plugin, and finally injected in the WebView.
import { dirname, extname, basename, resolve } from 'path';
import * as esbuild from 'esbuild';
import copyAssets from './copyAssets';
import { writeFile, readFile } from 'fs-extra';
export default class BundledFile {
private readonly bundleOutputPathBase_: string;
private readonly bundleBaseName_: string;
private readonly rootFileDirectory_: string;
public constructor(
public readonly bundleName: string,
private readonly sourceFilePath_: string,
) {
this.rootFileDirectory_ = dirname(sourceFilePath_);
this.bundleBaseName_ = basename(sourceFilePath_, extname(sourceFilePath_));
this.bundleOutputPathBase_ = `${this.rootFileDirectory_}/${this.bundleBaseName_}.bundle`;
}
private makeBuildContext(mode: 'production' | 'development') {
return esbuild.context({
entryPoints: [this.sourceFilePath_],
outfile: `${this.bundleOutputPathBase_}.js`,
minify: mode === 'production',
bundle: true,
sourcemap: true,
format: 'iife',
globalName: this.bundleName,
metafile: false,
// es5: Have Webpack's generated code target ES5. This doesn't apply to code not
// generated by Webpack.
target: ['chrome58', 'safari14'],
plugins: [
{
name: 'joplin--node-polyfills',
setup: build => {
build.onResolve({ filter: /^(path|events)$/ }, args => {
let path = args.path;
if (args.path === 'path') {
path = require.resolve('path-browserify');
} else if (args.path === 'events') {
path = require.resolve('events/');
}
return { path };
});
},
},
{
// Supports require(...)ing SVG images
name: 'joplin--require-svg',
setup: build => {
// A relative path to an SVG:
build.onResolve({ filter: /^\.{1,2}\/.*\.svg$/ }, args => ({
path: resolve(args.resolveDir, args.path),
namespace: 'joplin-require-svg',
}));
build.onLoad({ filter: /^.*$/, namespace: 'joplin-require-svg' }, async args => {
const fileContent = await readFile(args.path, 'utf-8');
return { contents: `
let svg = null;
export default () => {
svg ??= (() => {
const parser = new DOMParser();
const doc = parser.parseFromString(${JSON.stringify(fileContent)}, 'image/svg+xml');
return doc.querySelector('svg');
})();
return svg.cloneNode(true);
};
` };
});
},
},
{
name: 'joplin--copy-final',
setup: build => {
build.onEnd(async (result) => {
if (result.errors.length === 0) {
console.log('copy output');
await this.copyToImportableFile_();
} else {
console.warn('Copying skipped. Build produced errors');
}
});
},
},
],
});
}
// Creates a file that can be imported by React native. This file contains the
// bundled JS as a string.
private async copyToImportableFile_() {
await copyAssets(`${this.bundleName}.bundle`, {
js: `${this.bundleOutputPathBase_}.js`,
css: `${this.bundleOutputPathBase_}.css`,
});
}
// Create a minified JS file in the same directory as `this.sourceFilePath` with
// the same name.
public async build() {
console.info(`Building bundle: ${this.bundleName}...`);
const compiler = await this.makeBuildContext('production');
const result = await compiler.rebuild();
await compiler.dispose();
if (result?.metafile) {
await writeFile(`${this.bundleOutputPathBase_}.meta.json`, JSON.stringify(result.metafile, undefined, '\t'));
}
}
public async startWatching() {
console.info(`Watching bundle: ${this.bundleName}...`);
const compiler = await this.makeBuildContext('development');
await compiler.watch();
}
}