Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9eac574
fix(smoke-v2): run ci smoke tests in Playwright container
CharlieHelps May 15, 2026
6470539
chore(repo): configure safe.directory in smoke v2 workflow
CharlieHelps May 15, 2026
2e636c7
fix(ci): install xz-utils in smoke v2 workflow
CharlieHelps May 15, 2026
f1e8164
ci(repo): keep smoke-v2 task shell changes
CharlieHelps May 15, 2026
37a23c6
fix(smoke-v2): restore single-quoted moon schema
CharlieHelps May 15, 2026
c8d02f7
fix(cli): serialize preview watcher rebuilds
CharlieHelps May 15, 2026
3a4ebda
fix(ci): harden smoke-v2 preview process handling
CharlieHelps May 15, 2026
cc1f07b
fix(smoke-v2): launch preview webServer via email binary
CharlieHelps May 15, 2026
94e60cf
fix(smoke-v2): launch webServer via bash start script
CharlieHelps May 15, 2026
3cb8f74
ci(repo): add windows runners to test workflows
CharlieHelps May 15, 2026
3fc2d98
fix(create-mail): dereference templates symlink when copying generators
CharlieHelps May 15, 2026
546728b
fix: resolve Windows CI path regressions
CharlieHelps May 15, 2026
578ad61
test(cli): normalize create-mail snapshot paths
CharlieHelps May 15, 2026
d042959
test(cli): sort create-mail snapshot file list deterministically
CharlieHelps May 15, 2026
5ae33fb
fix(ci): normalize watcher node_modules path matching on Windows
CharlieHelps May 15, 2026
2a19c1f
test(smoke-v2): skip watcher scenario on Windows CI
CharlieHelps May 15, 2026
338ec12
chore: revert quote changes
shellscape May 16, 2026
6a7c4e4
Merge branch 'main' into charlie/windows-ci-support
shellscape May 16, 2026
9b85427
chore: revert serial queue slop
shellscape May 16, 2026
782a293
chore: node_modules watcher clarity on windows
shellscape May 16, 2026
5fe7bc7
chore: watcher path normalization on windows
shellscape May 16, 2026
44271e9
chore: child path refinements
shellscape May 16, 2026
cd77c9f
chore: watcher avoids duplicate events
shellscape May 16, 2026
bb1b692
chore: watcher delete cleanup
shellscape May 16, 2026
99b8849
chore: remove smoke skip for windows
shellscape May 16, 2026
dfea204
chore: watcher improvements and test coverage
shellscape May 16, 2026
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
6 changes: 6 additions & 0 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
name: Setup
description: Sets up the CI environment

inputs:
moon-cache:
description: Enable moonrepo/setup-toolchain caching
default: 'true'

runs:
using: 'composite'
steps:
Expand All @@ -11,6 +16,7 @@ runs:
- name: Setup Moon
uses: moonrepo/setup-toolchain@v0
with:
cache: ${{ inputs.moon-cache }}
moon-version: 1.41.2

- name: Setup Node
Expand Down
14 changes: 12 additions & 2 deletions .github/workflows/test-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,18 @@ on:

jobs:
validate:
runs-on: ubuntu-latest
name: CLI Tests
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest

runs-on: ${{ matrix.os }}
name: CLI Tests (${{ matrix.os }})

defaults:
run:
shell: bash

steps:
- name: Checkout Commit
Expand Down
63 changes: 61 additions & 2 deletions .github/workflows/test-smoke-v2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@ on:
- synchronize

jobs:
validate:
validate-ubuntu:
runs-on: ubuntu-latest
name: Smoke v2 Tests
name: Smoke v2 Tests (ubuntu-latest)

defaults:
run:
shell: bash

container:
image: mcr.microsoft.com/playwright:v1.57.0-noble
options: --ipc=host --init
Expand Down Expand Up @@ -54,3 +59,57 @@ jobs:
DOT_LOG_LEVEL: debug
LOCAL_SMOKE: 'true'
run: moon smoke-v2:run-ci

validate-windows:
runs-on: windows-latest
name: Smoke v2 Tests (windows-latest)

defaults:
run:
shell: bash

steps:
- name: Checkout Commit
uses: actions/checkout@v4
with:
fetch-depth: 10

- name: Configure git safe.directory
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: Checkout Main
run: |
git fetch origin
git branch -f main origin/main

- name: Ensure xz is available
run: |
if ! command -v xz >/dev/null 2>&1; then
if command -v sudo >/dev/null 2>&1; then
sudo apt-get update
sudo apt-get install -y xz-utils
else
apt-get update
apt-get install -y xz-utils
fi
fi

- name: Setup
uses: ./.github/actions/setup
with:
moon-cache: 'false'

- name: Install Playwright browser
run: pnpm --filter test-smoke-v2 exec playwright install chromium

- name: Build Projects
run: |
moon jsx-email:build
moon create-mail:build
moon run :build --query 'project~plugin-*'

- name: Run smoke-v2 tests
env:
DOT_LOG_LEVEL: debug
LOCAL_SMOKE: 'true'
run: moon smoke-v2:run-ci
14 changes: 12 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,18 @@ on:

jobs:
validate:
runs-on: ubuntu-latest
name: Run Tests
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest

runs-on: ${{ matrix.os }}
name: Run Tests (${{ matrix.os }})

defaults:
run:
shell: bash

steps:
- name: Checkout Commit
Expand Down
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,18 @@ run with `tsx`.

## Checks

Run checks through Moon (mirrors CI):
Run checks before considering work complete.

```bash
moon run :lint
moon run :format
moon run :typecheck
moon run :test
```

If `moon` isn’t available on your PATH, run it via `./node_modules/.bin/moon` (for example `./node_modules/.bin/moon run :lint`).

There is no repo-wide Moon format task. Format changed files directly with `./node_modules/.bin/oxfmt <files...>` before the final check pass.

`moon repo:build.all --cache off` is the canonical task to use for building (compiling) all packages.
Do not run `:compile` tasks directly. `:build` tasks can be called on projects outside of packages.

Expand Down
2 changes: 1 addition & 1 deletion packages/create-mail/moon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ tasks:
runDepsInParallel: false

copy:
command: cp -r generators dist
command: cp -rL generators dist
options:
cache: false

Expand Down
38 changes: 35 additions & 3 deletions packages/jsx-email/src/cli/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ interface BuildTemplateParams {
targetPath: string;
}

interface RelativeOutputDirParams {
baseDir: string;
outputBasePath: string;
pathApi?: Pick<typeof posix, 'isAbsolute' | 'relative' | 'sep'>;
}

interface BuildOptions {
argv: BuildCommandOptions;
outputBasePath?: string;
Expand All @@ -64,6 +70,26 @@ export interface BuildTempatesResult extends BuildResult {
fileName: string;
}

export const getRelativeOutputDir = ({
baseDir,
outputBasePath,
pathApi = isWindows ? win32 : posix
}: RelativeOutputDirParams) => {
const relativeOutputDir = pathApi.relative(outputBasePath, baseDir);

if (
!relativeOutputDir ||
relativeOutputDir === '.' ||
relativeOutputDir === '..' ||
relativeOutputDir.startsWith(`..${pathApi.sep}`) ||
pathApi.isAbsolute(relativeOutputDir)
) {
return null;
}

return relativeOutputDir;
};

export const help = chalkTmpl`
{blue email build}

Expand Down Expand Up @@ -105,8 +131,9 @@ export const getTempPath = async (type: 'build' | 'preview') => {
export const build = async (options: BuildOptions): Promise<BuildResult> => {
const { argv, outputBasePath, path, sourceFile } = options;
const { html = true, out, plain, props = '{}', usePreviewProps, writeToFile = true } = argv;
const compiledPath = isWindows ? pathToFileURL(normalizePath(path)).toString() : path;
const template = await import(compiledPath);
const compiledPath = normalizePath(path);
const importPath = isWindows ? pathToFileURL(compiledPath).toString() : compiledPath;
const template = await import(importPath);
// proper named export
const componentExport: TemplateFn = template.Template;

Expand All @@ -123,8 +150,13 @@ export const build = async (options: BuildOptions): Promise<BuildResult> => {
const templateName = basename(path, fileExt).replace(/-[^-]{8}$/, '');
const component = componentExport(renderProps);
const baseDir = dirname(path);
const relativeOutputDir = outputBasePath
? getRelativeOutputDir({ baseDir, outputBasePath })
: null;
const writePath = outputBasePath
? join(out!, baseDir.replace(outputBasePath, ''), templateName)
? relativeOutputDir
? join(out!, relativeOutputDir, templateName)
: join(out!, templateName)
: join(out!, templateName);
// const writePath = outputBasePath
// ? join(out!, baseDir.replace(outputBasePath, ''), templateName + extension)
Expand Down
Loading
Loading