mirror of
https://github.com/taskgenius/taskgenius-plugin.git
synced 2026-07-22 06:40:25 +00:00
build: configure release-it for automated releases
- Add release-it with conventional changelog support - Configure automatic GitHub releases with asset uploads - Add custom Obsidian version bumper plugin - Add zip script for packaging releases - Update package.json with release scripts - Install required dependencies (jszip, globby, release-it plugins)
This commit is contained in:
parent
9e240d77c6
commit
912789918b
9 changed files with 2715 additions and 5 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -36,12 +36,9 @@ package-lock.json
|
|||
.env
|
||||
|
||||
# translations
|
||||
scripts
|
||||
translation-templates
|
||||
._data.json
|
||||
|
||||
styles.css
|
||||
|
||||
CLAUDE.md
|
||||
.kiro
|
||||
.claude
|
||||
.claude
|
||||
|
|
|
|||
58
.release-it.cjs
Normal file
58
.release-it.cjs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
module.exports = {
|
||||
hooks: {
|
||||
"before:init": ["pnpm test", "pnpm run build"],
|
||||
"after:bump": [
|
||||
"pnpm run build",
|
||||
"node ./scripts/zip.mjs",
|
||||
"git add .",
|
||||
],
|
||||
"after:release":
|
||||
"echo Successfully released Task Genius v${version} to ${repo.repository}.",
|
||||
},
|
||||
git: {
|
||||
requireBranch: "master",
|
||||
requireCleanWorkingDir: true,
|
||||
pushArgs: "--follow-tags -o ci.skip",
|
||||
commitMessage: "chore(release): bump version to ${version}",
|
||||
tagName: "${version}",
|
||||
tagAnnotation: "Release v${version}",
|
||||
addUntrackedFiles: true,
|
||||
},
|
||||
plugins: {
|
||||
"@release-it/conventional-changelog": {
|
||||
preset: {
|
||||
name: "conventionalcommits",
|
||||
types: [
|
||||
{ type: "feat", section: "✨ Features" },
|
||||
{ type: "fix", section: "🐛 Bug Fixes" },
|
||||
{ type: "perf", section: "⚡ Performance" },
|
||||
{ type: "refactor", section: "♻️ Refactors" },
|
||||
{ type: "chore", section: "🔧 Chores" },
|
||||
{ type: "docs", section: "📝 Documentation" },
|
||||
{ type: "style", section: "💄 Styles" },
|
||||
{ type: "test", section: "✅ Tests" }
|
||||
]
|
||||
},
|
||||
infile: "CHANGELOG.md",
|
||||
header: "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n"
|
||||
},
|
||||
"./scripts/ob-bumper.mjs": {
|
||||
indent: 2,
|
||||
copyTo: "./dist",
|
||||
},
|
||||
},
|
||||
npm: {
|
||||
publish: false,
|
||||
},
|
||||
github: {
|
||||
release: true,
|
||||
assets: [
|
||||
"dist/main.js",
|
||||
"dist/manifest.json",
|
||||
"dist/styles.css",
|
||||
"dist/task-genius-${version}.zip",
|
||||
],
|
||||
proxy: process.env.HTTPS_PROXY,
|
||||
releaseName: "v${version}",
|
||||
},
|
||||
};
|
||||
12
package.json
12
package.json
|
|
@ -11,7 +11,12 @@
|
|||
"g-l": "cross-env node scripts/generate-locale-files.cjs",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"prepare": "husky"
|
||||
"prepare": "husky",
|
||||
"release": "release-it",
|
||||
"release:patch": "release-it patch",
|
||||
"release:minor": "release-it minor",
|
||||
"release:major": "release-it major",
|
||||
"release:dry": "release-it --dry-run"
|
||||
},
|
||||
"keywords": [
|
||||
"obsidian",
|
||||
|
|
@ -32,6 +37,8 @@
|
|||
"@codemirror/state": "^6.5.2",
|
||||
"@codemirror/view": "^6.36.7",
|
||||
"@datastructures-js/queue": "^4.2.3",
|
||||
"@release-it/bumper": "^7.0.5",
|
||||
"@release-it/conventional-changelog": "^10.0.1",
|
||||
"@types/jest": "^29.5.0",
|
||||
"@types/node": "^16.11.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.2.0",
|
||||
|
|
@ -41,12 +48,15 @@
|
|||
"cross-env": "^7.0.3",
|
||||
"esbuild": "0.13.12",
|
||||
"esbuild-plugin-inline-worker": "https://github.com/mitschabaude/esbuild-plugin-inline-worker",
|
||||
"globby": "^14.1.0",
|
||||
"husky": "^9.1.7",
|
||||
"jest": "^29.5.0",
|
||||
"jest-environment-jsdom": "^29.5.0",
|
||||
"jszip": "^3.10.1",
|
||||
"monkey-around": "^3.0.0",
|
||||
"obsidian": "^1.8.7",
|
||||
"regexp-match-indices": "^1.0.2",
|
||||
"release-it": "^19.0.4",
|
||||
"rrule": "^2.8.1",
|
||||
"ts-jest": "^29.1.0",
|
||||
"tslib": "2.4.0",
|
||||
|
|
|
|||
2094
pnpm-lock.yaml
2094
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
239
scripts/extract-translations.cjs
Normal file
239
scripts/extract-translations.cjs
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
const ts = require("typescript");
|
||||
|
||||
const SRC_DIR = path.resolve(__dirname, "../src");
|
||||
const TRANSLATIONS_DIR = path.resolve(SRC_DIR, "translations/locale");
|
||||
const BASE_LOCALE = "en";
|
||||
|
||||
// Define supported locales
|
||||
const SUPPORTED_LOCALES = [
|
||||
"ar",
|
||||
"cs",
|
||||
"da",
|
||||
"de",
|
||||
"en",
|
||||
"en-gb",
|
||||
"es",
|
||||
"fr",
|
||||
"hi",
|
||||
"id",
|
||||
"it",
|
||||
"ja",
|
||||
"ko",
|
||||
"nl",
|
||||
"no",
|
||||
"pl",
|
||||
"pt",
|
||||
"pt-br",
|
||||
"ro",
|
||||
"ru",
|
||||
"tr",
|
||||
"zh-cn",
|
||||
"zh-tw",
|
||||
"uk",
|
||||
];
|
||||
|
||||
function extractTranslationKeys(sourceFile) {
|
||||
const keys = [];
|
||||
|
||||
function visit(node) {
|
||||
if (
|
||||
ts.isCallExpression(node) &&
|
||||
ts.isIdentifier(node.expression) &&
|
||||
node.expression.text === "t"
|
||||
) {
|
||||
const firstArg = node.arguments[0];
|
||||
if (ts.isStringLiteral(firstArg)) {
|
||||
keys.push({
|
||||
key: firstArg.text,
|
||||
location: `${sourceFile.fileName}:${
|
||||
sourceFile.getLineAndCharacterOfPosition(
|
||||
node.getStart()
|
||||
).line + 1
|
||||
}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
ts.forEachChild(node, visit);
|
||||
}
|
||||
|
||||
visit(sourceFile);
|
||||
return keys;
|
||||
}
|
||||
|
||||
function scanDirectory(dir) {
|
||||
const allKeys = [];
|
||||
|
||||
function scan(currentDir) {
|
||||
const entries = fs.readdirSync(currentDir);
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(currentDir, entry);
|
||||
const stat = fs.statSync(fullPath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
scan(fullPath);
|
||||
} else if (stat.isFile() && /\.(ts|tsx)$/.test(entry)) {
|
||||
const sourceFile = ts.createSourceFile(
|
||||
fullPath,
|
||||
fs.readFileSync(fullPath, "utf-8"),
|
||||
ts.ScriptTarget.Latest,
|
||||
true
|
||||
);
|
||||
|
||||
allKeys.push(...extractTranslationKeys(sourceFile));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scan(dir);
|
||||
return allKeys;
|
||||
}
|
||||
|
||||
function loadTranslations(locale) {
|
||||
try {
|
||||
const filePath = path.join(TRANSLATIONS_DIR, `${locale}.ts`);
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return {};
|
||||
}
|
||||
const content = fs.readFileSync(filePath, "utf-8");
|
||||
const sourceFile = ts.createSourceFile(
|
||||
filePath,
|
||||
content,
|
||||
ts.ScriptTarget.Latest,
|
||||
true
|
||||
);
|
||||
|
||||
// Simple AST parsing to extract the translations object
|
||||
let translations = {};
|
||||
ts.forEachChild(sourceFile, (node) => {
|
||||
if (ts.isVariableStatement(node)) {
|
||||
const declarations = node.declarationList.declarations;
|
||||
if (declarations.length > 0) {
|
||||
const declaration = declarations[0];
|
||||
const initializer = declaration.initializer;
|
||||
if (
|
||||
initializer &&
|
||||
ts.isObjectLiteralExpression(initializer)
|
||||
) {
|
||||
initializer.properties.forEach((prop) => {
|
||||
if (
|
||||
ts.isPropertyAssignment(prop) &&
|
||||
ts.isStringLiteral(prop.initializer) &&
|
||||
ts.isIdentifier(prop.name)
|
||||
) {
|
||||
translations[prop.name.text] =
|
||||
prop.initializer.text;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return translations;
|
||||
} catch (error) {
|
||||
console.warn(`Failed to load translations for ${locale}:`, error);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function generateTemplates(extractedKeys) {
|
||||
const baseTranslations = loadTranslations(BASE_LOCALE);
|
||||
|
||||
// First, deduplicate keys while preserving all unique locations
|
||||
const uniqueKeys = extractedKeys.reduce((acc, { key, location }) => {
|
||||
if (!acc[key]) {
|
||||
acc[key] = { key, locations: [location] };
|
||||
} else {
|
||||
acc[key].locations.push(location);
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Generate template for each supported locale
|
||||
SUPPORTED_LOCALES.forEach((locale) => {
|
||||
const templatePath = path.resolve(
|
||||
__dirname,
|
||||
`../translation-templates/${locale}.json`
|
||||
);
|
||||
|
||||
// Load existing template if it exists
|
||||
let existingTemplate = {};
|
||||
try {
|
||||
if (fs.existsSync(templatePath)) {
|
||||
existingTemplate = JSON.parse(
|
||||
fs.readFileSync(templatePath, "utf-8")
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`Failed to load existing template for ${locale}:`,
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
const currentTranslations = loadTranslations(locale);
|
||||
const existingEntries = existingTemplate.entries || [];
|
||||
|
||||
// Create a map of existing translations for quick lookup
|
||||
const existingTranslationsMap = existingEntries.reduce((acc, entry) => {
|
||||
acc[entry.key] = entry;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Merge existing and new translations
|
||||
const entries = Object.values(uniqueKeys).map(({ key, locations }) => {
|
||||
const existing = existingTranslationsMap[key];
|
||||
let status = "UNTRANSLATED";
|
||||
let target = "";
|
||||
|
||||
if (existing) {
|
||||
// Preserve existing translation and status
|
||||
status = existing.status;
|
||||
target = existing.target;
|
||||
} else if (currentTranslations[key]) {
|
||||
status = "TRANSLATED";
|
||||
target = currentTranslations[key];
|
||||
} else if (locale !== BASE_LOCALE && baseTranslations[key]) {
|
||||
status = "UNTRANSLATED";
|
||||
}
|
||||
|
||||
return {
|
||||
key,
|
||||
status,
|
||||
locations,
|
||||
source: baseTranslations[key] || key,
|
||||
target,
|
||||
};
|
||||
});
|
||||
|
||||
const template = {
|
||||
language: locale,
|
||||
entries,
|
||||
lastUpdated: new Date().toISOString(),
|
||||
};
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
const outputDir = path.dirname(templatePath);
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
fs.writeFileSync(templatePath, JSON.stringify(template, null, 2));
|
||||
console.log(
|
||||
`Translation template for ${locale} written to: ${templatePath}`
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function main() {
|
||||
console.log("Scanning for translation keys...");
|
||||
const extractedKeys = scanDirectory(SRC_DIR);
|
||||
console.log(`Found ${extractedKeys.length} translation keys`);
|
||||
|
||||
generateTemplates(extractedKeys);
|
||||
}
|
||||
|
||||
main();
|
||||
124
scripts/generate-locale-files.cjs
Normal file
124
scripts/generate-locale-files.cjs
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const LOCALE_DIR = path.resolve(__dirname, "../src/translations/locale");
|
||||
const TEMPLATE_DIR = path.resolve(__dirname, "../translation-templates");
|
||||
|
||||
// Only generate the following locales
|
||||
const LOCALES = {
|
||||
"en-gb": "British English",
|
||||
en: "English",
|
||||
ja: "Japanese",
|
||||
"pt-br": "Brazilian Portuguese",
|
||||
ru: "Russian",
|
||||
uk: "Ukrainian",
|
||||
"zh-cn": "Simplified Chinese",
|
||||
"zh-tw": "Traditional Chinese",
|
||||
};
|
||||
|
||||
const TEMPLATE = (lang, translations) => `// ${lang} translations
|
||||
const translations = ${JSON.stringify(translations, null, 2)};
|
||||
|
||||
export default translations;
|
||||
`;
|
||||
|
||||
function loadTemplateTranslations(locale) {
|
||||
try {
|
||||
const templatePath = path.join(TEMPLATE_DIR, `${locale}.json`);
|
||||
if (!fs.existsSync(templatePath)) {
|
||||
return {};
|
||||
}
|
||||
const template = JSON.parse(fs.readFileSync(templatePath, "utf-8"));
|
||||
|
||||
// Try to load existing translations file if it exists
|
||||
let existingTranslations = {};
|
||||
const existingFilePath = path.join(LOCALE_DIR, `${locale}.ts`);
|
||||
if (fs.existsSync(existingFilePath)) {
|
||||
const content = fs.readFileSync(existingFilePath, "utf-8");
|
||||
const match = content.match(
|
||||
/const translations = (\{[\s\S]*?\n\});/
|
||||
);
|
||||
if (match && match[1]) {
|
||||
try {
|
||||
// Convert the matched string to a JavaScript object
|
||||
existingTranslations = eval(`(${match[1]})`);
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
`Failed to parse existing translations for ${locale}:`,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const translations = { ...existingTranslations };
|
||||
|
||||
// Only add untranslated entries to the end
|
||||
template.entries.forEach((entry) => {
|
||||
// Skip if already in existing translations
|
||||
if (translations[entry.key] !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the source text as the target if target is empty
|
||||
translations[entry.key] = entry.target || entry.source;
|
||||
|
||||
// Mark all generated entries as TRANSLATED
|
||||
if (template.entries.indexOf(entry) !== -1) {
|
||||
entry.status = "TRANSLATED";
|
||||
}
|
||||
});
|
||||
|
||||
// Write back the updated template with TRANSLATED status
|
||||
fs.writeFileSync(
|
||||
templatePath,
|
||||
JSON.stringify(template, null, 2),
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
return translations;
|
||||
} catch (error) {
|
||||
console.warn(`Failed to load template for ${locale}:`, error);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function generateLocaleFiles() {
|
||||
// Create locale directory if it doesn't exist
|
||||
if (!fs.existsSync(LOCALE_DIR)) {
|
||||
fs.mkdirSync(LOCALE_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
// Process each locale
|
||||
for (const [locale, language] of Object.entries(LOCALES)) {
|
||||
const templatePath = path.join(TEMPLATE_DIR, `${locale}.json`);
|
||||
|
||||
if (fs.existsSync(templatePath)) {
|
||||
console.log(
|
||||
`\nProcessing translations for ${language} (${locale})...`
|
||||
);
|
||||
|
||||
// Generate the locale file
|
||||
const filename = `${locale}.ts`;
|
||||
const filepath = path.join(LOCALE_DIR, filename);
|
||||
const translations = loadTemplateTranslations(locale);
|
||||
|
||||
fs.writeFileSync(filepath, TEMPLATE(language, translations));
|
||||
console.log(`Generated ${filename}`);
|
||||
} else {
|
||||
console.warn(`Template file not found for ${locale}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Main function
|
||||
function main() {
|
||||
try {
|
||||
generateLocaleFiles();
|
||||
} catch (error) {
|
||||
console.error("Error during locale file generation:", error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
121
scripts/ob-bumper.mjs
Normal file
121
scripts/ob-bumper.mjs
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
import { copyFile, readFile, writeFile } from "fs/promises";
|
||||
import { join } from "path";
|
||||
import { Plugin } from "release-it";
|
||||
import semverPrerelease from "semver/functions/prerelease.js";
|
||||
|
||||
const mainManifest = "manifest.json",
|
||||
betaManifest = "manifest-beta.json",
|
||||
versionsList = "versions.json";
|
||||
const targets = [mainManifest, betaManifest, versionsList];
|
||||
|
||||
const isPreRelease = (version) => semverPrerelease(version) !== null;
|
||||
|
||||
class ObsidianVersionBump extends Plugin {
|
||||
async readJson(path) {
|
||||
// const { isDryRun } = this.config;
|
||||
try {
|
||||
const result = JSON.parse(await readFile(path, "utf8"));
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (error.code === "ENOENT") {
|
||||
return null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async writeJson(file, data) {
|
||||
// const { isDryRun } = this.config;
|
||||
const { indent = 4 } = this.getContext();
|
||||
await writeFile(file, JSON.stringify(data, null, indent));
|
||||
// this.log.exec(`Write to ${file}`, isDryRun);
|
||||
}
|
||||
|
||||
/**
|
||||
* always read from previous version of manifest
|
||||
*/
|
||||
async readManifest() {
|
||||
const { isDryRun } = this.config;
|
||||
const latest = isPreRelease(this.config.contextOptions.latestVersion);
|
||||
let manifestToRead = this.getManifest(latest);
|
||||
this.log.exec(`Reading manifest from ${manifestToRead}`, isDryRun);
|
||||
let manifest;
|
||||
if (!(manifest = await this.readJson(manifestToRead))) {
|
||||
manifestToRead = this.getManifest(!latest);
|
||||
this.log.exec(`retry reading manifest from ${manifestToRead}`, isDryRun);
|
||||
manifest = await this.readJson(manifestToRead);
|
||||
}
|
||||
if (!manifest) throw new Error("Missing manifest data");
|
||||
return manifest;
|
||||
}
|
||||
|
||||
async writeManifest(targetVersion, manifest) {
|
||||
const { isDryRun } = this.config;
|
||||
const manifestToWrite = this.getManifest(isPreRelease(targetVersion));
|
||||
const updatedMainfest = { ...manifest, version: targetVersion };
|
||||
!isDryRun && (await this.writeJson(manifestToWrite, updatedMainfest));
|
||||
this.log.exec(
|
||||
`Wrote version ${targetVersion} to ${manifestToWrite}`,
|
||||
isDryRun,
|
||||
);
|
||||
await this.syncManifest(targetVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* if bump to official release
|
||||
* sync manifest-beta.json with manifest.json
|
||||
*/
|
||||
async syncManifest(targetVersion) {
|
||||
const target = isPreRelease(targetVersion);
|
||||
const { isDryRun } = this.config;
|
||||
if (!target) {
|
||||
!isDryRun && (await copyFile(mainManifest, betaManifest));
|
||||
this.log.exec(`Syncing ${betaManifest} with ${mainManifest}`, isDryRun);
|
||||
}
|
||||
}
|
||||
|
||||
async copyToRoot() {
|
||||
const { copyTo } = this.getContext();
|
||||
if (!copyTo) return;
|
||||
const { isDryRun } = this.config;
|
||||
if (!isDryRun) {
|
||||
await Promise.all(
|
||||
targets.map((file) =>
|
||||
copyFile(file, join(copyTo, file)).catch((err) => {
|
||||
if (err.code !== "ENOENT") throw err;
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
this.log.exec(`Copied ${targets.join(", ")} to ${copyTo}`, isDryRun);
|
||||
}
|
||||
|
||||
/**
|
||||
* update versions.json with target version and minAppVersion from manifest.json
|
||||
*/
|
||||
async writeVersion(targetVersion, { minAppVersion }) {
|
||||
const { isDryRun } = this.config;
|
||||
const versions = await this.readJson(versionsList);
|
||||
versions[targetVersion] = minAppVersion;
|
||||
!isDryRun && (await this.writeJson(versionsList, versions));
|
||||
this.log.exec(
|
||||
`Wrote version ${targetVersion} to ${versionsList}`,
|
||||
isDryRun,
|
||||
);
|
||||
}
|
||||
|
||||
getManifest(isPreRelease) {
|
||||
return isPreRelease ? betaManifest : mainManifest;
|
||||
}
|
||||
|
||||
async bump(targetVersion) {
|
||||
// read minAppVersion from manifest and bump version to target version
|
||||
const manifest = await this.readManifest(targetVersion);
|
||||
this.log.info(`min obsidian app version: ${manifest.minAppVersion}`);
|
||||
await Promise.all([
|
||||
this.writeManifest(targetVersion, manifest),
|
||||
this.writeVersion(targetVersion, manifest),
|
||||
]);
|
||||
await this.copyToRoot();
|
||||
}
|
||||
}
|
||||
export default ObsidianVersionBump;
|
||||
45
scripts/ob.esbuild.mjs
Normal file
45
scripts/ob.esbuild.mjs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { copyFile, rename, writeFile } from "fs/promises";
|
||||
import { dirname, join } from "path";
|
||||
|
||||
/**
|
||||
* @param {{ hotreload?: boolean, beta?:boolean }} config
|
||||
* @returns {import("esbuild").Plugin}
|
||||
*/
|
||||
const obPlugin = (config = {}) => ({
|
||||
name: "obsidian-plugin",
|
||||
setup: (build) => {
|
||||
const { hotreload = true, beta = false } = config;
|
||||
build.onEnd(async () => {
|
||||
const outDir = dirname(build.initialOptions.outfile ?? "main.js");
|
||||
// fix default css output file name
|
||||
const { outfile } = build.initialOptions;
|
||||
try {
|
||||
await rename(
|
||||
outfile.replace(/\.js$/, ".css"),
|
||||
outfile.replace(/main\.js$/, "styles.css"),
|
||||
);
|
||||
} catch (err) {
|
||||
if (err.code !== "ENOENT") throw err;
|
||||
}
|
||||
|
||||
// copy manifest.json to build dir
|
||||
if (!beta) {
|
||||
await copyFile("manifest.json", join(outDir, "manifest.json"));
|
||||
} else {
|
||||
await copyFile("manifest-beta.json", join(outDir, "manifest.json"));
|
||||
}
|
||||
|
||||
// create .hotreload if it doesn't exist
|
||||
if (hotreload) {
|
||||
try {
|
||||
await writeFile(join(outDir, ".hotreload"), "", { flag: "wx" });
|
||||
} catch (err) {
|
||||
if (err.code !== "EEXIST") throw err;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("build finished");
|
||||
});
|
||||
},
|
||||
});
|
||||
export default obPlugin;
|
||||
22
scripts/zip.mjs
Normal file
22
scripts/zip.mjs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import {createReadStream, createWriteStream, readFileSync} from "fs";
|
||||
import {rm} from "fs/promises";
|
||||
import JSZip from "jszip";
|
||||
import {join} from "path";
|
||||
import {pipeline} from "stream/promises";
|
||||
import {globby} from "globby"
|
||||
|
||||
const assets = ["main.js", "styles.css", "manifest.json"];
|
||||
|
||||
await globby("dist/*.zip").then(files => Promise.all(files.map(f => rm(f))))
|
||||
|
||||
const zip = new JSZip();
|
||||
for (const filename of assets) {
|
||||
zip.file(filename, createReadStream(join("dist", filename)));
|
||||
}
|
||||
const version = JSON.parse(readFileSync(join("package.json"), "utf-8")).version;
|
||||
const out = join("dist", `task-genius-${version}.zip`)
|
||||
await pipeline(
|
||||
zip.generateNodeStream({type: "nodebuffer", streamFiles: true, compression: "DEFLATE"}),
|
||||
createWriteStream(out),
|
||||
);
|
||||
console.log(out + " written.");
|
||||
Loading…
Reference in a new issue