Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ shimの限界として、正しく動作しないものはeslintによってエ
| Photoshop | `phxs` | `src/phxs/` |

`pnpm add-app -- --app=<appId>` で新しいアプリを追加できます。
追加できるのは `aeft`, `ilst`, `phxs`, `idsn`, `ppro`, `anmt`, `audt` の正式対応アプリのみです。

## 環境 / Environment

Expand Down Expand Up @@ -112,6 +113,7 @@ entry("example", () => {
pnpm build # 変更のあるスクリプトをビルド
pnpm build --all # 全スクリプトを強制ビルド
pnpm build --app=aeft # 特定アプリのみビルド
pnpm build:aeft # After Effects のスクリプトのみビルド
```

出力先は `dist/{appId}/{ScriptName}/` です。
Expand All @@ -129,13 +131,18 @@ pnpm watch
| `pnpm build` | 変更のあるスクリプトをビルド |
| `pnpm build --all` | 全スクリプトを強制ビルド |
| `pnpm build --app=aeft` | 特定アプリのみビルド |
| `pnpm build:aeft` | After Effects のみビルド |
| `pnpm build:ilst` | Illustrator のみビルド |
| `pnpm build:phxs` | Photoshop のみビルド |
| `pnpm watch` | ファイル変更を監視して自動ビルド |
| `pnpm lint` | ESLint でコード検査 |
| `pnpm format` | Prettier でコード整形 |
| `pnpm new` | 新規スクリプト追加 |
| `pnpm add-app` | 新規アプリ追加 |
| `pnpm clean` | ビルドハッシュをクリーンアップ |

`pnpm add-app -- --app=<appId>` で正式対応アプリを追加すると、`pnpm build:<appId>` も自動で追加されます。

## テスト / Test

`src/tests/index.ts`にテストを記述しています。
Expand Down
13 changes: 12 additions & 1 deletion docs/guides/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,21 @@ pnpm build

```bash
pnpm build --app=aeft
pnpm build:aeft
```

`pnpm build:aeft` は `pnpm build --app=aeft` と同じく、After Effects 向けスクリプトだけをビルドする短い別名です。
Illustrator は `pnpm build:ilst`、Photoshop は `pnpm build:phxs` を使えます。

### 新しいアプリを追加

```bash
pnpm add-app -- --app=idsn
```

追加できるのは `aeft`, `ilst`, `phxs`, `idsn`, `ppro`, `anmt`, `audt` の正式対応アプリのみです。
追加したアプリには `pnpm build:<appId>` も自動で追加されます。

## ディレクトリ構造

```
Expand Down Expand Up @@ -112,7 +119,8 @@ entry("MyScript", () => {
#### `entryUI` — ScriptUI パネル対応スクリプト用

ドッキングパネルとして使う場合は `entryUI` と `__ES_THIS__` を使います。
`__ES_THIS__` はビルド時にバンドル先頭へ自動注入されるグローバルな `this` です。
`__ES_THIS__` はビルド時にバンドル先頭へ `var __ES_THIS__=this;` として自動注入されるグローバルな `this` です。
Rollup 出力は `"use strict";` を出さない設定にしているため、ScriptUI パネル起動時の `this` を保持できます。

```ts
import { entryUI } from "../../lib/lib";
Expand All @@ -128,6 +136,9 @@ entryUI("MyScript", __ES_THIS__, (win) => {
| スクリプトとして実行 | グローバルオブジェクト | `new Window("palette")` を生成して表示 |
| ドッキングパネルとして起動 | `Panel` | 渡された `Panel` をそのまま使用 |

`entryUI` は UI 構築中や palette 表示時の例外をエラーダイアログで表示し、同じ例外を再送出します。
`onClick` などのイベントハンドラ内で処理を行う場合は、処理本体を `entry()` で囲んでください。

```ts
import { entry, alertError } from "../../lib/lib";
```
Expand Down
48 changes: 31 additions & 17 deletions docs/project-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,12 @@ entryUI("MyScript", __ES_THIS__, (win) => {
});
```

`__ES_THIS__` はビルド時にバンドル先頭へ自動注入されるグローバルな `this` で、
Extension Manager / Dockable パネルとして起動された場合は `Panel`、
スクリプトとして実行された場合はグローバルオブジェクトを返す。
`__ES_THIS__` はビルド時にバンドル先頭へ `var __ES_THIS__=this;` として自動注入される。
Rollup 出力は `"use strict";` を出さない設定にしているため、After Effects の ScriptUI パネルとして起動された場合は `Panel`、
スクリプトとして直接実行された場合はグローバルオブジェクトを保持する。

通常の手書き JSX であれば `createUI(this)` のように直接 `this` を渡せる。
このテンプレートでは TypeScript / Rollup / Terser の変換後も同じ値を保つため、出力先頭で退避した `__ES_THIS__` を `entryUI` に渡す。

### 予約名

Expand All @@ -159,7 +162,8 @@ entry("MyScript", () => {
```

- 実行時に Undo グループを作成し、完了後に閉じる
- 例外が発生した場合は `alertError()` でエラーダイアログを表示する
- 例外が発生した場合は `alertError()` でエラーダイアログを表示し、同じ例外を再送出する
- 例外が発生しても Undo グループは必ず閉じる

### `entryUI` — ScriptUI ウィンドウ用

Expand All @@ -179,6 +183,7 @@ entryUI("MyScript", __ES_THIS__, (win) => {
```

- `entryUI` はウィンドウ・パネルの UI を組み立てるための関数で、Undo グループは作らない
- UI 構築中や palette 表示時の例外はエラーダイアログを表示し、同じ例外を再送出する
- **`onClick` などのイベントハンドラ内で処理を行う場合は、必ず `entry` で囲むこと**
- こうすることで処理ごとに Undo グループが作られ、エラーハンドリングも機能する

Expand All @@ -188,27 +193,36 @@ entryUI("MyScript", __ES_THIS__, (win) => {
pnpm add-app -- --app=idsn
```

追加できるのは `aeft`, `ilst`, `phxs`, `idsn`, `ppro`, `anmt`, `audt` の正式対応アプリのみ。
任意のアプリID追加は未対応。

実行すると以下を自動生成:

- `src/{app}/tsconfig.json`(types-for-adobe のマッピング付き
- `src/{app}/tsconfig.json`(types-for-adobe と ScriptUI 共通型のマッピング付き
- `src/{app}/types/index.d.ts`
- `src/{app}/lib/.gitkeep`
- `src/{app}/example/index.ts`
- `es.config.mjs` に `scripts.{app}` キーを追加
- `es.config.mjs` に `scripts.{app}` キーを追加し、`example` を `build: true`, `license: true` で登録
- `package.json` に `build:<appId>` コマンドを追加

既存の `src/{appId}` または `es.config.mjs` の `scripts.{appId}` と衝突する場合は、ファイルを生成せずに停止する。

## 開発コマンド

| コマンド | 説明 |
| ----------------------- | ---------------------------------- |
| `pnpm build` | 変更のあるスクリプトをビルド |
| `pnpm build --all` | 全スクリプトを強制ビルド |
| `pnpm build --app=aeft` | 特定アプリのスクリプトのみビルド |
| `pnpm watch` | ファイル変更を監視して自動ビルド |
| `pnpm lint` | ESLint でコード検査 |
| `pnpm format` | Prettier でコード整形 |
| `pnpm new` | 新規スクリプト追加(対話式 / CLI) |
| `pnpm add-app` | 新規アプリスキャフォールディング |
| `pnpm clean` | ビルドハッシュをクリーンアップ |
| コマンド | 説明 |
| ----------------------- | ------------------------------------ |
| `pnpm build` | 変更のあるスクリプトをビルド |
| `pnpm build --all` | 全スクリプトを強制ビルド |
| `pnpm build --app=aeft` | 特定アプリのスクリプトのみビルド |
| `pnpm build:aeft` | After Effects のスクリプトのみビルド |
| `pnpm build:ilst` | Illustrator のスクリプトのみビルド |
| `pnpm build:phxs` | Photoshop のスクリプトのみビルド |
| `pnpm watch` | ファイル変更を監視して自動ビルド |
| `pnpm lint` | ESLint でコード検査 |
| `pnpm format` | Prettier でコード整形 |
| `pnpm new` | 新規スクリプト追加(対話式 / CLI) |
| `pnpm add-app` | 新規アプリスキャフォールディング |
| `pnpm clean` | ビルドハッシュをクリーンアップ |

## 型情報について

Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
"description": "Multi-app ExtendScript TypeScript template for Adobe After Effects, Illustrator, Photoshop, and more",
"scripts": {
"build": "rollup -c",
"build:aeft": "rollup -c --app=aeft",
"build:ilst": "rollup -c --app=ilst",
"build:phxs": "rollup -c --app=phxs",
"clean": "node ./scripts/cleanBuildHashes.mjs",
"watch": "rollup -c -w",
"lint": "eslint .",
"format": "prettier --write .",
"new": "node ./scripts/newScript.mjs&&prettier --write ./es.config.mjs",
"new": "node ./scripts/newScript.mjs",
"add-app": "node ./scripts/addApp.mjs"
},
"keywords": [
Expand Down
92 changes: 73 additions & 19 deletions rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -40,49 +40,103 @@ const hashText = (text) => {
return hashSum.digest("hex");
};

const collectTypeScriptFiles = (targetDir) => {
if (!fs.existsSync(targetDir)) {
const normalizePath = (filePath) => filePath.replace(/\\/g, "/");

const collectFiles = (targetPath) => {
if (!fs.existsSync(targetPath)) {
return [];
}

const stats = fs.statSync(targetPath);
if (stats.isFile()) {
return [path.resolve(targetPath)];
}

if (!stats.isDirectory()) {
return [];
}

const entries = fs.readdirSync(targetDir, { withFileTypes: true });
const entries = fs.readdirSync(targetPath, { withFileTypes: true });
const files = [];

entries.forEach((entry) => {
const fullPath = path.join(targetDir, entry.name);
const fullPath = path.join(targetPath, entry.name);

if (entry.isDirectory()) {
files.push(...collectTypeScriptFiles(fullPath));
files.push(...collectFiles(fullPath));
return;
}

if (/\.ts$/i.test(entry.name)) {
files.push(fullPath);
if (entry.isFile()) {
files.push(path.resolve(fullPath));
}
});

return files.sort((left, right) => left.localeCompare(right));
return files;
};

const getUniqueSortedFiles = (inputPaths) => {
const fileMap = new Map();

inputPaths.forEach((inputPath) => {
collectFiles(inputPath).forEach((filePath) => {
fileMap.set(filePath, filePath);
});
});

return Array.from(fileMap.values()).sort((left, right) =>
normalizePath(left).localeCompare(normalizePath(right))
);
};

const calculateScriptHash = (scriptDir) => {
const files = collectTypeScriptFiles(scriptDir);
const calculateInputHash = (inputPaths) => {
const files = getUniqueSortedFiles(inputPaths);

if (files.length === 0) {
return hashText("empty");
}

const merged = files
.map((filePath) => {
const relativePath = path
.relative(scriptDir, filePath)
.replace(/\\/g, "/");
const relativePath = normalizePath(path.relative(".", filePath));
return `${relativePath}:${calculateFileHash(filePath)}`;
})
.join("|");

return hashText(merged);
};

const SHARED_BUILD_INPUTS = [
"rollup.config.mjs",
"es.config.mjs",
"package.json",
"pnpm-lock.yaml",
"tsconfig.json",
"src/init.ts",
"src/lib",
"src/types",
];

const getLicenseFile = (srcDir) =>
fs.existsSync(`${srcDir}/LICENSE`) ? `${srcDir}/LICENSE` : "LICENSE";

const getScriptHashInputs = ({ appId, script, srcDir, tsconfig }) => {
const inputs = [...SHARED_BUILD_INPUTS, srcDir, tsconfig];

if (appId) {
inputs.push(`src/${appId}`);
}

if (script.license) {
inputs.push(getLicenseFile(srcDir));
}

return inputs;
};

const calculateScriptHash = (scriptContext) =>
calculateInputHash(getScriptHashInputs(scriptContext));

const loadBuildHashes = () => {
try {
if (!fs.existsSync(BUILD_HASH_FILE)) {
Expand Down Expand Up @@ -173,9 +227,7 @@ const extractCommentsToTop = () => ({
});

const licenser = (srcDir) => {
const licenseDir = fs.existsSync(`${srcDir}/LICENSE`)
? `${srcDir}/LICENSE`
: "LICENSE";
const licenseDir = getLicenseFile(srcDir);
return license({
banner: {
content: {
Expand Down Expand Up @@ -268,10 +320,11 @@ export default (commandLineArgs) => {
}

const previousBuildHashes = loadBuildHashes();
const currentBuildHashes = {};
const currentBuildHashes = appFilter ? { ...previousBuildHashes } : {};

const targetScripts = allScripts.filter(({ script, srcDir, hashKey }) => {
const scriptHash = calculateScriptHash(srcDir);
const targetScripts = allScripts.filter((scriptContext) => {
const { hashKey } = scriptContext;
const scriptHash = calculateScriptHash(scriptContext);
currentBuildHashes[hashKey] = scriptHash;

if (forceBuildAll) {
Expand All @@ -294,6 +347,7 @@ export default (commandLineArgs) => {
output: {
file: `${outDir}/${script.name}.jsx`,
format: "cjs",
strict: false,
},
context: "this",
onwarn,
Expand Down
Loading
Loading