mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-07-27 17:14:35 +02:00
Merge branch 'develop' into room
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
import * as fs from 'node:fs/promises';
|
||||
import * as path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import * as yaml from 'js-yaml';
|
||||
import { load as loadYaml } from 'js-yaml';
|
||||
import { buildTarball } from './tarball.mjs';
|
||||
|
||||
const configDir = fileURLToPath(new URL('../.config', import.meta.url));
|
||||
@@ -17,16 +17,11 @@ const configPath = process.env.MISSKEY_CONFIG_YML
|
||||
: path.resolve(configDir, 'default.yml');
|
||||
|
||||
async function loadConfig() {
|
||||
return fs.readFile(configPath, 'utf-8').then(data => yaml.load(data)).catch(() => null);
|
||||
}
|
||||
|
||||
async function copyFrontendFonts() {
|
||||
await fs.cp('./packages/frontend/node_modules/three/examples/fonts', './built/_frontend_dist_/fonts', { dereference: true, recursive: true });
|
||||
return fs.readFile(configPath, 'utf-8').then(data => loadYaml(data)).catch(() => null);
|
||||
}
|
||||
|
||||
async function build() {
|
||||
await Promise.all([
|
||||
copyFrontendFonts(),
|
||||
loadConfig().then(config => config?.publishTarballInsteadOfProvideRepositoryUrl && buildTarball()),
|
||||
]);
|
||||
}
|
||||
|
||||
3
scripts/changelog-checker/.gitignore
vendored
3
scripts/changelog-checker/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
node_modules
|
||||
coverage
|
||||
.idea
|
||||
@@ -1,17 +0,0 @@
|
||||
import tsParser from '@typescript-eslint/parser';
|
||||
import sharedConfig from '../../packages/shared/eslint.config.js';
|
||||
|
||||
export default [
|
||||
...sharedConfig,
|
||||
{
|
||||
files: ['src/**/*.ts', 'src/**/*.tsx'],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
parser: tsParser,
|
||||
project: ['./tsconfig.json'],
|
||||
sourceType: 'module',
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
2412
scripts/changelog-checker/package-lock.json
generated
2412
scripts/changelog-checker/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"name": "changelog-checker",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"run": "vite-node src/index.ts",
|
||||
"test": "vitest run",
|
||||
"test:coverage": "vitest run --coverage"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mdast": "4.0.4",
|
||||
"@types/node": "26.0.0",
|
||||
"@vitest/coverage-v8": "4.1.9",
|
||||
"mdast-util-to-string": "4.0.0",
|
||||
"remark": "15.0.1",
|
||||
"remark-parse": "11.0.0",
|
||||
"typescript": "5.9.3",
|
||||
"unified": "11.0.5",
|
||||
"vite": "8.1.0",
|
||||
"vite-node": "6.0.0",
|
||||
"vitest": "4.1.9"
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Release } from './parser.js';
|
||||
|
||||
export class Result {
|
||||
public readonly success: boolean;
|
||||
public readonly message?: string;
|
||||
|
||||
private constructor(success: boolean, message?: string) {
|
||||
this.success = success;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
static ofSuccess(): Result {
|
||||
return new Result(true);
|
||||
}
|
||||
|
||||
static ofFailed(message?: string): Result {
|
||||
return new Result(false, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* develop -> masterまたはrelease -> masterを想定したパターン。
|
||||
* base側の先頭とhead側で追加された分のリリースより1つ前のバージョンが等価であるかチェックする。
|
||||
*/
|
||||
export function checkNewRelease(base: Release[], head: Release[]): Result {
|
||||
const releaseCountDiff = head.length - base.length;
|
||||
if (releaseCountDiff <= 0) {
|
||||
return Result.ofFailed('Invalid release count.');
|
||||
}
|
||||
|
||||
const baseLatest = base[0];
|
||||
const headPrevious = head[releaseCountDiff];
|
||||
|
||||
if (baseLatest.releaseName !== headPrevious.releaseName) {
|
||||
return Result.ofFailed('Contains unexpected releases.');
|
||||
}
|
||||
|
||||
return Result.ofSuccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* topic -> developまたはtopic -> masterを想定したパターン。
|
||||
* head側の最新リリース配下に書き加えられているかをチェックする。
|
||||
*/
|
||||
export function checkNewTopic(base: Release[], head: Release[]): Result {
|
||||
if (head.length !== base.length) {
|
||||
return Result.ofFailed('Invalid release count.');
|
||||
}
|
||||
|
||||
const headLatest = head[0];
|
||||
for (let relIdx = 0; relIdx < base.length; relIdx++) {
|
||||
const baseItem = base[relIdx];
|
||||
const headItem = head[relIdx];
|
||||
if (baseItem.releaseName !== headItem.releaseName) {
|
||||
// リリースの順番が変わってると成立しないのでエラーにする
|
||||
return Result.ofFailed(`Release is different. base:${baseItem.releaseName}, head:${headItem.releaseName}`);
|
||||
}
|
||||
|
||||
if (baseItem.categories.length !== headItem.categories.length) {
|
||||
// カテゴリごと書き加えられたパターン
|
||||
if (headLatest.releaseName !== headItem.releaseName) {
|
||||
// 最新リリース以外に追記されていた場合
|
||||
return Result.ofFailed(`There is an error in the update history. expected additions:${headLatest.releaseName}, actual additions:${headItem.releaseName}`);
|
||||
}
|
||||
} else {
|
||||
// カテゴリ数の変動はないのでリスト項目の数をチェック
|
||||
for (let catIdx = 0; catIdx < baseItem.categories.length; catIdx++) {
|
||||
const baseCategory = baseItem.categories[catIdx];
|
||||
const headCategory = headItem.categories[catIdx];
|
||||
|
||||
if (baseCategory.categoryName !== headCategory.categoryName) {
|
||||
// カテゴリの順番が変わっていると成立しないのでエラーにする
|
||||
return Result.ofFailed(`Category is different. base:${baseCategory.categoryName}, head:${headCategory.categoryName}`);
|
||||
}
|
||||
|
||||
if (baseCategory.items.length !== headCategory.items.length) {
|
||||
if (headLatest.releaseName !== headItem.releaseName) {
|
||||
// 最新リリース以外に追記されていた場合
|
||||
return Result.ofFailed(`There is an error in the update history. expected additions:${headLatest.releaseName}, actual additions:${headItem.releaseName}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result.ofSuccess();
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import * as process from 'process';
|
||||
import * as fs from 'fs';
|
||||
import { parseChangeLog } from './parser.js';
|
||||
import { checkNewRelease, checkNewTopic } from './checker.js';
|
||||
|
||||
function abort(message?: string) {
|
||||
if (message) {
|
||||
console.error(message);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function main() {
|
||||
if (!fs.existsSync('./CHANGELOG-base.md') || !fs.existsSync('./CHANGELOG-head.md')) {
|
||||
console.error('CHANGELOG-base.md or CHANGELOG-head.md is missing.');
|
||||
return;
|
||||
}
|
||||
|
||||
const base = parseChangeLog('./CHANGELOG-base.md');
|
||||
const head = parseChangeLog('./CHANGELOG-head.md');
|
||||
|
||||
const result = (base.length < head.length)
|
||||
? checkNewRelease(base, head)
|
||||
: checkNewTopic(base, head);
|
||||
|
||||
if (!result.success) {
|
||||
abort(result.message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import * as fs from 'node:fs';
|
||||
import { unified } from 'unified';
|
||||
import remarkParse from 'remark-parse';
|
||||
import { Heading, List, Node } from 'mdast';
|
||||
import { toString } from 'mdast-util-to-string';
|
||||
|
||||
export class Release {
|
||||
public readonly releaseName: string;
|
||||
public readonly categories: ReleaseCategory[];
|
||||
|
||||
constructor(releaseName: string, categories: ReleaseCategory[] = []) {
|
||||
this.releaseName = releaseName;
|
||||
this.categories = [...categories];
|
||||
}
|
||||
}
|
||||
|
||||
export class ReleaseCategory {
|
||||
public readonly categoryName: string;
|
||||
public readonly items: string[];
|
||||
|
||||
constructor(categoryName: string, items: string[] = []) {
|
||||
this.categoryName = categoryName;
|
||||
this.items = [...items];
|
||||
}
|
||||
}
|
||||
|
||||
function isHeading(node: Node): node is Heading {
|
||||
return node.type === 'heading';
|
||||
}
|
||||
|
||||
function isList(node: Node): node is List {
|
||||
return node.type === 'list';
|
||||
}
|
||||
|
||||
export function parseChangeLog(path: string): Release[] {
|
||||
const input = fs.readFileSync(path, { encoding: 'utf8' });
|
||||
const processor = unified().use(remarkParse);
|
||||
|
||||
const releases: Release[] = [];
|
||||
const root = processor.parse(input);
|
||||
|
||||
let release: Release | null = null;
|
||||
let category: ReleaseCategory | null = null;
|
||||
for (const it of root.children) {
|
||||
if (isHeading(it) && it.depth === 2) {
|
||||
// リリース
|
||||
release = new Release(toString(it));
|
||||
releases.push(release);
|
||||
} else if (isHeading(it) && it.depth === 3 && release) {
|
||||
// リリース配下のカテゴリ
|
||||
category = new ReleaseCategory(toString(it));
|
||||
release.categories.push(category);
|
||||
} else if (isList(it) && category) {
|
||||
for (const listItem of it.children) {
|
||||
// カテゴリ配下のリスト項目
|
||||
category.items.push(toString(listItem));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
@@ -1,419 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import {expect, suite, test} from "vitest";
|
||||
import {Release, ReleaseCategory} from "../src/parser";
|
||||
import {checkNewRelease, checkNewTopic} from "../src/checker";
|
||||
|
||||
suite('checkNewRelease', () => {
|
||||
test('headに新しいリリースがある1', () => {
|
||||
const base = [new Release('2024.12.0')]
|
||||
const head = [new Release('2024.12.1'), new Release('2024.12.0')]
|
||||
|
||||
const result = checkNewRelease(base, head)
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test('headに新しいリリースがある2', () => {
|
||||
const base = [new Release('2024.12.0')]
|
||||
const head = [new Release('2024.12.2'), new Release('2024.12.1'), new Release('2024.12.0')]
|
||||
|
||||
const result = checkNewRelease(base, head)
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
|
||||
test('リリースの数が同じ', () => {
|
||||
const base = [new Release('2024.12.0')]
|
||||
const head = [new Release('2024.12.0')]
|
||||
|
||||
const result = checkNewRelease(base, head)
|
||||
|
||||
console.log(result.message)
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
test('baseにあるリリースがheadにない', () => {
|
||||
const base = [new Release('2024.12.0')]
|
||||
const head = [new Release('2024.12.2'), new Release('2024.12.1')]
|
||||
|
||||
const result = checkNewRelease(base, head)
|
||||
|
||||
console.log(result.message)
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
suite('checkNewTopic', () => {
|
||||
test('追記なし', () => {
|
||||
const base = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
new ReleaseCategory('Client', [
|
||||
'feat3',
|
||||
'feat4',
|
||||
])
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
new ReleaseCategory('Client', [
|
||||
'feat3',
|
||||
'feat4',
|
||||
])
|
||||
])
|
||||
]
|
||||
|
||||
const head = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
new ReleaseCategory('Client', [
|
||||
'feat3',
|
||||
'feat4',
|
||||
])
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
new ReleaseCategory('Client', [
|
||||
'feat3',
|
||||
'feat4',
|
||||
])
|
||||
])
|
||||
]
|
||||
|
||||
const result = checkNewTopic(base, head)
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test('最新バージョンにカテゴリを追加したときはエラーにならない', () => {
|
||||
const base = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
new ReleaseCategory('Client', [
|
||||
'feat3',
|
||||
'feat4',
|
||||
])
|
||||
])
|
||||
]
|
||||
|
||||
const head = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
new ReleaseCategory('Client', [
|
||||
'feat3',
|
||||
'feat4',
|
||||
])
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
new ReleaseCategory('Client', [
|
||||
'feat3',
|
||||
'feat4',
|
||||
])
|
||||
])
|
||||
]
|
||||
|
||||
const result = checkNewTopic(base, head)
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test('最新バージョンからカテゴリを削除したときはエラーにならない', () => {
|
||||
const base = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
new ReleaseCategory('Client', [
|
||||
'feat3',
|
||||
'feat4',
|
||||
])
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
new ReleaseCategory('Client', [
|
||||
'feat3',
|
||||
'feat4',
|
||||
])
|
||||
])
|
||||
]
|
||||
|
||||
const head = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
new ReleaseCategory('Client', [
|
||||
'feat3',
|
||||
'feat4',
|
||||
])
|
||||
])
|
||||
]
|
||||
|
||||
const result = checkNewTopic(base, head)
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test('最新バージョンに追記したときはエラーにならない', () => {
|
||||
const base = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
])
|
||||
]
|
||||
|
||||
const head = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
'feat3',
|
||||
]),
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
])
|
||||
]
|
||||
|
||||
const result = checkNewTopic(base, head)
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test('最新バージョンから削除したときはエラーにならない', () => {
|
||||
const base = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
])
|
||||
]
|
||||
|
||||
const head = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
]),
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
])
|
||||
]
|
||||
|
||||
const result = checkNewTopic(base, head)
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
test('古いバージョンにカテゴリを追加したときはエラーになる', () => {
|
||||
const base = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
])
|
||||
]
|
||||
|
||||
const head = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
new ReleaseCategory('Client', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
])
|
||||
]
|
||||
|
||||
const result = checkNewTopic(base, head)
|
||||
|
||||
console.log(result.message)
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
test('古いバージョンからカテゴリを削除したときはエラーになる', () => {
|
||||
const base = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
])
|
||||
]
|
||||
|
||||
const head = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
])
|
||||
]
|
||||
|
||||
const result = checkNewTopic(base, head)
|
||||
|
||||
console.log(result.message)
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
test('古いバージョンに追記したときはエラーになる', () => {
|
||||
const base = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
])
|
||||
]
|
||||
|
||||
const head = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
'feat3',
|
||||
]),
|
||||
])
|
||||
]
|
||||
|
||||
const result = checkNewTopic(base, head)
|
||||
|
||||
console.log(result.message)
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
test('古いバージョンから削除したときはエラーになる', () => {
|
||||
const base = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
])
|
||||
]
|
||||
|
||||
const head = [
|
||||
new Release('2024.12.1', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
'feat2',
|
||||
]),
|
||||
]),
|
||||
new Release('2024.12.0', [
|
||||
new ReleaseCategory('Server', [
|
||||
'feat1',
|
||||
]),
|
||||
])
|
||||
]
|
||||
|
||||
const result = checkNewTopic(base, head)
|
||||
|
||||
console.log(result.message)
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "nodenext",
|
||||
"moduleResolution": "nodenext",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"removeComments": true,
|
||||
"strict": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictNullChecks": true,
|
||||
"experimentalDecorators": true,
|
||||
"noImplicitReturns": true,
|
||||
"esModuleInterop": true,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types"
|
||||
],
|
||||
"lib": [
|
||||
"esnext"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"test/**/*"
|
||||
]
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import {defineConfig} from 'vite';
|
||||
|
||||
|
||||
const config = defineConfig({});
|
||||
|
||||
export default config;
|
||||
198
scripts/check-dts.mjs
Normal file
198
scripts/check-dts.mjs
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import ts from 'typescript';
|
||||
|
||||
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const defaultRootDir = path.resolve(scriptDir, '..');
|
||||
|
||||
// パス判定は OS 差分を避けるため、必要なところだけ POSIX 形式へ寄せる。
|
||||
function toPosixPath(filePath) {
|
||||
return filePath.split(path.sep).join('/');
|
||||
}
|
||||
|
||||
function isInside(parentDir, filePath) {
|
||||
const relative = path.relative(parentDir, filePath);
|
||||
return relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative);
|
||||
}
|
||||
|
||||
// skipLibCheck で隠したくない、リポジトリ管理下の手書き .d.ts だけを検査する。
|
||||
export function isCheckableDeclarationFile(filePath, rootDir = defaultRootDir) {
|
||||
const absoluteRootDir = path.resolve(rootDir);
|
||||
const absoluteFilePath = path.resolve(filePath);
|
||||
if (!isInside(absoluteRootDir, absoluteFilePath)) return false;
|
||||
if (!absoluteFilePath.endsWith('.d.ts')) return false;
|
||||
|
||||
const relativePath = toPosixPath(path.relative(absoluteRootDir, absoluteFilePath));
|
||||
const segments = relativePath.split('/');
|
||||
if (segments.includes('node_modules') || segments.includes('.pnpm')) return false;
|
||||
if (segments.includes('built') || segments.includes('js-built')) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// root package.json の workspaces を信頼して、検査対象 package を列挙する。
|
||||
function readJson(filePath) {
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
}
|
||||
|
||||
function getWorkspacePackageDirs(rootDir) {
|
||||
const rootPackageJson = readJson(path.join(rootDir, 'package.json'));
|
||||
return rootPackageJson.workspaces
|
||||
.filter((workspace) => !workspace.includes('*'))
|
||||
.map((workspace) => path.resolve(rootDir, workspace))
|
||||
.filter((workspaceDir) => fs.existsSync(path.join(workspaceDir, 'package.json')));
|
||||
}
|
||||
|
||||
function getTsconfigPaths(packageDir) {
|
||||
return ts.sys.readDirectory(
|
||||
packageDir,
|
||||
['.json'],
|
||||
['node_modules'],
|
||||
['**/tsconfig.json'],
|
||||
undefined,
|
||||
);
|
||||
}
|
||||
|
||||
// tsconfig の include から漏れる package 直下の shim も拾えるよう、補助的に探索する。
|
||||
function discoverDeclarationFiles(packageDir, rootDir) {
|
||||
return ts.sys.readDirectory(
|
||||
packageDir,
|
||||
['.d.ts'],
|
||||
undefined,
|
||||
['**/*.d.ts'],
|
||||
undefined,
|
||||
).filter((filePath) => isCheckableDeclarationFile(filePath, rootDir));
|
||||
}
|
||||
|
||||
// 各 package の設定をそのまま使い、検査時だけ skipLibCheck を後で上書きする。
|
||||
function readTsconfig(tsconfigPath) {
|
||||
if (!fs.existsSync(tsconfigPath)) return undefined;
|
||||
|
||||
const config = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
|
||||
if (config.error != null) {
|
||||
throw new Error(ts.flattenDiagnosticMessageText(config.error.messageText, '\n'));
|
||||
}
|
||||
|
||||
return ts.parseJsonConfigFileContent(config.config, ts.sys, path.dirname(tsconfigPath));
|
||||
}
|
||||
|
||||
function createFormatHost(rootDir) {
|
||||
return {
|
||||
getCanonicalFileName: (fileName) => fileName,
|
||||
getCurrentDirectory: () => rootDir,
|
||||
getNewLine: () => ts.sys.newLine,
|
||||
};
|
||||
}
|
||||
|
||||
// 通常の tsconfig は include 対象を使い、root tsconfig だけ package 直下の shim を足す。
|
||||
function isRootTsconfig(packageDir, tsconfigPath) {
|
||||
return path.resolve(packageDir, 'tsconfig.json') === path.resolve(tsconfigPath);
|
||||
}
|
||||
|
||||
function getDeclarationFilesForTsconfig(packageDir, tsconfigPath, parsedConfig, rootDir) {
|
||||
const declarationFiles = parsedConfig.fileNames
|
||||
.filter((filePath) => isCheckableDeclarationFile(filePath, rootDir));
|
||||
|
||||
if (!isRootTsconfig(packageDir, tsconfigPath)) {
|
||||
return declarationFiles;
|
||||
}
|
||||
|
||||
const extraRootConfigDeclarationFiles = discoverDeclarationFiles(packageDir, rootDir)
|
||||
.filter((filePath) => {
|
||||
return path.dirname(filePath) === packageDir;
|
||||
});
|
||||
|
||||
return [...new Set([...declarationFiles, ...extraRootConfigDeclarationFiles])];
|
||||
}
|
||||
|
||||
// node_modules 側の診断は出さず、対象 .d.ts 自身に出た診断だけを失敗扱いにする。
|
||||
function checkTsconfigDeclarations(packageDir, tsconfigPath, rootDir) {
|
||||
const parsedConfig = readTsconfig(tsconfigPath);
|
||||
if (parsedConfig == null) {
|
||||
return { declarationFiles: [], diagnostics: [] };
|
||||
}
|
||||
|
||||
const declarationFiles = getDeclarationFilesForTsconfig(packageDir, tsconfigPath, parsedConfig, rootDir);
|
||||
if (declarationFiles.length === 0) {
|
||||
return { declarationFiles, diagnostics: [] };
|
||||
}
|
||||
|
||||
const program = ts.createProgram({
|
||||
rootNames: declarationFiles,
|
||||
options: {
|
||||
...parsedConfig.options,
|
||||
noEmit: true,
|
||||
skipLibCheck: false,
|
||||
},
|
||||
});
|
||||
|
||||
const diagnostics = ts.getPreEmitDiagnostics(program)
|
||||
.filter((diagnostic) => diagnostic.file != null && isCheckableDeclarationFile(diagnostic.file.fileName, rootDir));
|
||||
|
||||
return { declarationFiles, diagnostics };
|
||||
}
|
||||
|
||||
// 複数 tsconfig で同じ宣言を読むことがあるため、同一診断をまとめる。
|
||||
function deduplicateDiagnostics(diagnostics) {
|
||||
const seen = new Set();
|
||||
return diagnostics.filter((diagnostic) => {
|
||||
const key = [
|
||||
diagnostic.file?.fileName ?? '',
|
||||
diagnostic.start ?? '',
|
||||
diagnostic.code,
|
||||
ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'),
|
||||
].join('\0');
|
||||
if (seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function checkPackageDeclarations(packageDir, rootDir) {
|
||||
const results = getTsconfigPaths(packageDir)
|
||||
.map((tsconfigPath) => checkTsconfigDeclarations(packageDir, tsconfigPath, rootDir));
|
||||
|
||||
return {
|
||||
declarationFiles: [...new Set(results.flatMap((result) => result.declarationFiles))],
|
||||
diagnostics: deduplicateDiagnostics(results.flatMap((result) => result.diagnostics)),
|
||||
};
|
||||
}
|
||||
|
||||
// workspace 全体を走査し、CI が扱いやすい件数と診断リストに集約する。
|
||||
export function checkDeclarations(rootDir = defaultRootDir) {
|
||||
const workspacePackageDirs = getWorkspacePackageDirs(rootDir);
|
||||
const results = workspacePackageDirs.map((packageDir) => ({
|
||||
packageDir,
|
||||
...checkPackageDeclarations(packageDir, rootDir),
|
||||
}));
|
||||
|
||||
return {
|
||||
results,
|
||||
declarationFileCount: results.reduce((count, result) => count + result.declarationFiles.length, 0),
|
||||
diagnostics: results.flatMap((result) => result.diagnostics),
|
||||
};
|
||||
}
|
||||
|
||||
// CLI 実行時は TypeScript 標準の formatter で、通常の tsc に近い形で表示する。
|
||||
function main() {
|
||||
const { declarationFileCount, diagnostics } = checkDeclarations(defaultRootDir);
|
||||
|
||||
if (diagnostics.length === 0) {
|
||||
console.log(`Checked ${declarationFileCount} repository declaration files.`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(ts.formatDiagnosticsWithColorAndContext(diagnostics, createFormatHost(defaultRootDir)));
|
||||
process.exitCode = 1;
|
||||
}
|
||||
|
||||
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
||||
main();
|
||||
}
|
||||
25
scripts/check-dts.test.mjs
Normal file
25
scripts/check-dts.test.mjs
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { isCheckableDeclarationFile } from './check-dts.mjs';
|
||||
|
||||
const rootDir = '/repo';
|
||||
|
||||
test('detects repository-owned declaration files that should be checked', () => {
|
||||
assert.equal(isCheckableDeclarationFile(`${rootDir}/packages/frontend/@types/theme.d.ts`, rootDir), true);
|
||||
assert.equal(isCheckableDeclarationFile(`${rootDir}/packages/frontend/src/utility/virtual.d.ts`, rootDir), true);
|
||||
assert.equal(isCheckableDeclarationFile(`${rootDir}/packages/backend/test/global.d.ts`, rootDir), true);
|
||||
});
|
||||
|
||||
test('ignores declarations outside the repository-owned surface', () => {
|
||||
assert.equal(isCheckableDeclarationFile(`${rootDir}/node_modules/@types/node/index.d.ts`, rootDir), false);
|
||||
assert.equal(isCheckableDeclarationFile(`${rootDir}/packages/frontend/node_modules/@types/foo/index.d.ts`, rootDir), false);
|
||||
assert.equal(isCheckableDeclarationFile(`${rootDir}/node_modules/.pnpm/typescript/lib/lib.dom.d.ts`, rootDir), false);
|
||||
assert.equal(isCheckableDeclarationFile(`${rootDir}/packages/misskey-js/built/index.d.ts`, rootDir), false);
|
||||
assert.equal(isCheckableDeclarationFile(`${rootDir}/packages/frontend-shared/js-built/i18n.d.ts`, rootDir), false);
|
||||
assert.equal(isCheckableDeclarationFile(`${rootDir}/packages/frontend/src/theme.ts`, rootDir), false);
|
||||
});
|
||||
312
scripts/dev.mjs
312
scripts/dev.mjs
@@ -10,104 +10,268 @@ import { execa } from 'execa';
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
|
||||
await execa('pnpm', ['clean'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
/** @type {Set<import('execa').ResultPromise>} */
|
||||
const childProcesses = new Set();
|
||||
/** @type {Set<import('execa').ResultPromise>} */
|
||||
const persistentChildProcesses = new Set();
|
||||
let shuttingDown = false;
|
||||
let persistentChildProcessesStarted = false;
|
||||
let persistentChildProcessFailed = false;
|
||||
/** @type {Promise<void> | null} */
|
||||
let shutdownPromise = null;
|
||||
|
||||
/**
|
||||
* 開発用コマンドを起動し、終了時にまとめて停止できるよう追跡する。
|
||||
* Windows では Ctrl+C の配信先を分離し、出力を親コンソールへ中継する。
|
||||
*
|
||||
* @param {string} command - 実行するコマンド。
|
||||
* @param {string[]} args - コマンドへ渡す引数。
|
||||
* @param {import('execa').Options} options - execa の起動オプション。
|
||||
* @returns {import('execa').ResultPromise} 起動した子プロセス。
|
||||
*/
|
||||
function spawnChildProcess(command, args, options) {
|
||||
const isWindows = process.platform === 'win32';
|
||||
const shouldForceColor = (process.stdout.isTTY || process.stderr.isTTY)
|
||||
&& process.env.FORCE_COLOR == null
|
||||
&& process.env.NO_COLOR == null;
|
||||
const pnpmPath = _dirname + '/../node_modules/pnpm/bin/pnpm.mjs';
|
||||
const windowsCommand = [process.execPath, pnpmPath, ...args]
|
||||
.map(argument => `"${argument}"`)
|
||||
.join(' ');
|
||||
const executable = isWindows && command === 'pnpm' ? process.env.ComSpec ?? 'cmd.exe' : command;
|
||||
const executableArgs = isWindows && command === 'pnpm'
|
||||
? ['/d', '/s', '/c', `start "" /b /wait ${windowsCommand}`]
|
||||
: args;
|
||||
const childProcess = execa(executable, executableArgs, isWindows ? {
|
||||
...options,
|
||||
// `start /b` keeps the process in the current console without forwarding
|
||||
// Ctrl+C, allowing only this supervisor to coordinate the shutdown.
|
||||
windowsVerbatimArguments: true,
|
||||
windowsHide: false,
|
||||
env: shouldForceColor ? { FORCE_COLOR: '1' } : undefined,
|
||||
stdout: 'pipe',
|
||||
stderr: 'pipe',
|
||||
buffer: false,
|
||||
} : options);
|
||||
|
||||
if (isWindows) {
|
||||
if (options.stdout != null) childProcess.stdout?.pipe(options.stdout, { end: false });
|
||||
if (options.stderr != null) childProcess.stderr?.pipe(options.stderr, { end: false });
|
||||
}
|
||||
|
||||
childProcesses.add(childProcess);
|
||||
return childProcess;
|
||||
}
|
||||
|
||||
/**
|
||||
* 子プロセスの終了を待機し、追跡対象から取り除く。
|
||||
*
|
||||
* @param {string} command - 実行するコマンド。
|
||||
* @param {string[]} args - コマンドへ渡す引数。
|
||||
* @param {import('execa').Options} options - execa の起動オプション。
|
||||
* @returns {Promise<import('execa').Result>} 子プロセスの実行結果。
|
||||
*/
|
||||
async function runChildProcess(command, args, options) {
|
||||
const childProcess = spawnChildProcess(command, args, options);
|
||||
|
||||
try {
|
||||
return await childProcess;
|
||||
} finally {
|
||||
childProcesses.delete(childProcess);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 常駐する子プロセスがすべて終了していれば、終了結果を引き継いで親も終了する。
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function shutdownIfAllPersistentChildProcessesStopped() {
|
||||
if (shuttingDown || !persistentChildProcessesStarted || persistentChildProcesses.size > 0) return;
|
||||
|
||||
void shutdown(persistentChildProcessFailed ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 常駐する子プロセスを起動し、終了時の追跡解除・エラー出力・親の終了判定を設定する。
|
||||
*
|
||||
* @param {string} command - 実行するコマンド。
|
||||
* @param {string[]} args - コマンドへ渡す引数。
|
||||
* @param {import('execa').Options} options - execa の起動オプション。
|
||||
* @returns {void}
|
||||
*/
|
||||
function startChildProcess(command, args, options) {
|
||||
const childProcess = spawnChildProcess(command, args, options);
|
||||
persistentChildProcesses.add(childProcess);
|
||||
|
||||
void childProcess.then(() => {
|
||||
childProcesses.delete(childProcess);
|
||||
persistentChildProcesses.delete(childProcess);
|
||||
shutdownIfAllPersistentChildProcessesStopped();
|
||||
}, error => {
|
||||
childProcesses.delete(childProcess);
|
||||
persistentChildProcesses.delete(childProcess);
|
||||
if (!shuttingDown) {
|
||||
persistentChildProcessFailed = true;
|
||||
console.error(error);
|
||||
shutdownIfAllPersistentChildProcessesStopped();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 子プロセスとその配下のプロセスを停止し、終了を待機する。
|
||||
*
|
||||
* @param {import('execa').ResultPromise} childProcess - 停止する子プロセス。
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function stopChildProcess(childProcess) {
|
||||
if (process.platform === 'win32' && childProcess.pid != null) {
|
||||
const result = await execa('taskkill', ['/pid', childProcess.pid.toString(), '/t', '/f'], {
|
||||
reject: false,
|
||||
});
|
||||
if (result.failed) childProcess.kill();
|
||||
} else {
|
||||
childProcess.kill();
|
||||
}
|
||||
|
||||
await childProcess.catch(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 追跡中の子プロセスを一度だけ停止して、指定した終了コードで終了する。
|
||||
*
|
||||
* @param {number} exitCode - 親プロセスに返す終了コード。
|
||||
* @returns {Promise<void>} 実行中または完了した停止処理。
|
||||
*/
|
||||
function shutdown(exitCode) {
|
||||
if (shutdownPromise != null) return shutdownPromise;
|
||||
|
||||
shuttingDown = true;
|
||||
shutdownPromise = (async () => {
|
||||
await Promise.allSettled([...childProcesses].map(stopChildProcess));
|
||||
process.exit(exitCode);
|
||||
})();
|
||||
|
||||
return shutdownPromise;
|
||||
}
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
void shutdown(0);
|
||||
});
|
||||
|
||||
// アセットのビルドで依存しているので一番最初に必要
|
||||
await execa('pnpm', ['--filter', 'i18n', 'build'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
process.on('SIGTERM', () => {
|
||||
void shutdown(0);
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
execa('pnpm', ['build-pre'], {
|
||||
try {
|
||||
await runChildProcess('pnpm', ['clean'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
}),
|
||||
execa('pnpm', ['build-assets'], {
|
||||
});
|
||||
|
||||
// アセットのビルドで依存しているので一番最初に必要
|
||||
await runChildProcess('pnpm', ['--filter', 'i18n', 'build'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
}),
|
||||
execa('pnpm', ['--filter', 'backend...', '--filter=!backend', 'build'], {
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
runChildProcess('pnpm', ['build-pre'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
}),
|
||||
runChildProcess('pnpm', ['build-assets'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
}),
|
||||
runChildProcess('pnpm', ['--filter', 'backend...', '--filter=!backend', 'build'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
}),
|
||||
// icons-subsetterは開発段階では使用されないが、型エラーを抑制するためにはじめの一度だけビルドする
|
||||
runChildProcess('pnpm', ['--filter', 'icons-subsetter', 'build'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
}),
|
||||
runChildProcess('pnpm', ['--filter', 'misskey-js', 'build'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
}),
|
||||
]);
|
||||
|
||||
startChildProcess('pnpm', ['build-pre', '--watch'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
}),
|
||||
// icons-subsetterは開発段階では使用されないが、型エラーを抑制するためにはじめの一度だけビルドする
|
||||
execa('pnpm', ['--filter', 'icons-subsetter', 'build'], {
|
||||
});
|
||||
|
||||
startChildProcess('pnpm', ['build-assets', '--watch'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
}),
|
||||
execa('pnpm', ['--filter', 'misskey-js', 'build'], {
|
||||
});
|
||||
|
||||
startChildProcess('pnpm', ['--filter', 'backend', 'dev'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
execa('pnpm', ['build-pre', '--watch'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
startChildProcess('pnpm', ['--filter', 'frontend', 'watch'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
|
||||
execa('pnpm', ['build-assets', '--watch'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
startChildProcess('pnpm', ['--filter', 'frontend-embed', 'watch'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
|
||||
execa('pnpm', ['--filter', 'backend', 'dev'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
startChildProcess('pnpm', ['--filter', 'sw', 'watch'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
|
||||
execa('pnpm', ['--filter', 'frontend', 'watch'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
startChildProcess('pnpm', ['--filter', 'misskey-js', 'watch', '--no-clean'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
|
||||
execa('pnpm', ['--filter', 'frontend-embed', 'watch'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
startChildProcess('pnpm', ['--filter', 'i18n', 'watch', '--no-clean'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
|
||||
execa('pnpm', ['--filter', 'sw', 'watch'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
startChildProcess('pnpm', ['--filter', 'misskey-reversi', 'watch', '--no-clean'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
|
||||
execa('pnpm', ['--filter', 'misskey-js', 'watch', '--no-clean'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
startChildProcess('pnpm', ['--filter', 'misskey-bubble-game', 'watch', '--no-clean'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
|
||||
execa('pnpm', ['--filter', 'i18n', 'watch', '--no-clean'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
|
||||
execa('pnpm', ['--filter', 'misskey-reversi', 'watch', '--no-clean'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
|
||||
execa('pnpm', ['--filter', 'misskey-bubble-game', 'watch', '--no-clean'], {
|
||||
cwd: _dirname + '/../',
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
persistentChildProcessesStarted = true;
|
||||
shutdownIfAllPersistentChildProcessesStopped();
|
||||
} catch (error) {
|
||||
if (!shuttingDown) {
|
||||
console.error(error);
|
||||
await shutdown(1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user