Skip to content

Add playground-to-example sync infrastructure#468

Merged
yamcodes merged 21 commits intomainfrom
copilot/consolidate-playgrounds-examples
Dec 9, 2025
Merged

Add playground-to-example sync infrastructure#468
yamcodes merged 21 commits intomainfrom
copilot/consolidate-playgrounds-examples

Conversation

Copy link
Contributor

Copilot AI commented Nov 29, 2025

Playgrounds and examples were maintained separately, causing drift and duplication. This adds tooling to make playgrounds the source of truth and auto-generate examples from them.

New bin/sync-examples.js script

  • Reads arkenvExamples metadata from playground package.json
  • Copies files, transforming workspace:*^<version> and catalog:^<version>
  • Syncs README.md and all source files from playgrounds
  • Preserves example-specific files (lockfiles, .gitignore)
  • Excludes playground-specific files (.cursor, eslint.config.js, pnpm-lock.yaml)
  • --check flag for CI verification

Playground metadata

{
  "arkenvExamples": [
    {
      "name": "with-bun",
      "packageManager": "bun"
    }
  ]
}

Mappings

All examples are now synced from playgrounds:

Playground Example
node basic
js basic-js
bun with-bun
bun-react with-bun-react
vite with-vite-react

New js playground

Created a new js playground for plain JavaScript examples, mirroring the node playground but without TypeScript.

CI workflow

New .github/workflows/sync-examples.yml verifies examples are in sync on PRs touching playgrounds or examples.

Usage

pnpm sync:examples        # Sync all
pnpm sync:examples:check  # Verify sync (CI)
Original prompt

This section details on the original issue you should resolve

<issue_title>Consolidate Playgrounds and Examples</issue_title>
<issue_description>It's too much trouble to maintain both the playgrounds and the examples. We need to consolidate them somehow.

The thing is, examples need to be isolated and standalone so they can be easily set up using git clone or a CLI tool. On the other hand, playgrounds need to use workspace packages so we can test new features in them before we publish our packages.

More info


We maintain two sets of consumer apps in this repo:

  • Playgrounds (under apps/playgrounds/)

    Used for local development and testing unpublished changes via workspace:*

  • Examples (under examples/)

    Standalone templates intended for users to clone and run directly


To avoid duplication and drift, playgrounds are the source of truth and examples are automatically generated from them.


Goals

  • Update example apps automatically when playgrounds change

  • Allow some playgrounds to remain internal-only

  • Support multi-example generation from a single playground

  • Allow per-example configuration (package manager, name, etc.)

  • Keep README.md and code consistent between environments


Metadata in Playground 

package.json


Playgrounds that should generate examples declare metadata in package.json:

{
  "name": "@arkenv/playground-vite-react",
  "private": true,
  "dependencies": {
    "arkenv": "workspace:*"
  },
  "arkenvExamples": [
    {
      "name": "with-vite-react",
      "packageManager": "npm"
    }
  ]
}

Behavior:

  • If arkenvExamples is missing → no example is generated

  • Each object in the array → one generated example


Optional future metadata fields:

{
  "description": "ArkEnv in Vite + React",
  "variant": "js",
  "include": ["src", "public"],
  "exclude": ["tests"]
}


Sync Script


A script (e.g. bin/sync-examples.ts) will:

  1. Iterate through playgrounds with arkenvExamples

  2. Copy their content into examples/<name>

  3. Modify package.json in the example:

    • Remove "private": true

    • Replace internal deps:

"arkenv": "workspace:*"  →  "arkenv": "^<published-version>"


    • Adjust scripts if needed based on packageManager

  1. Copy README.md from playground, unchanged (must work in both contexts)


Example folder structure after sync:

examples/with-vite-react/
├── README.md
├── package.json
├── src/
└── public/


Continuous Integration


PR Validation (Required)

  • Run the sync ...


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Summary by CodeRabbit

Release Notes

  • New Features

    • Automated example synchronization system enabling playgrounds to sync to published examples via arkenvExamples metadata
    • GitHub Actions workflow validates example synchronization on each pull request
  • Chores

    • Updated example projects (Bun, Bun+React, Vite+React) with new configurations and environment variables
    • Bumped dependencies including React, arkenv, arktype, and TypeScript tooling
    • Enhanced TypeScript configurations for stricter module syntax handling and type checking
  • Documentation

    • Updated examples README with synced and manually maintained examples workflow guidance

✏️ Tip: You can customize this high-level summary in your review settings.

@changeset-bot
Copy link

changeset-bot bot commented Nov 29, 2025

⚠️ No Changeset found

Latest commit: c2158c1

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 29, 2025

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Implements a playground-to-examples synchronization system where playgrounds serve as the canonical source of truth. A new sync script automatically generates standalone examples from playgrounds by copying files, transforming dependencies from workspace references to published versions, and adjusting package.json configurations per target package manager.

Changes

Cohort / File(s) Summary
GitHub Actions & Tooling
.github/workflows/sync-examples.yml, package.json
Added new workflow sync-examples to validate example synchronization on PRs and pushes to main. Added sync:examples and sync:examples:check npm scripts.
Synchronization Script
bin/sync-examples.js
New Node.js script that copies playgrounds to examples, transforming workspace dependencies to published versions, adjusting package.json per target package manager, and providing check-mode validation with diff reporting.
Playground Metadata
apps/playgrounds/bun-react/package.json, apps/playgrounds/bun/package.json, apps/playgrounds/vite/package.json
Added arkenvExamples metadata field declaring example configurations (name, packageManager) to each playground. Updated Bun playground scripts to target specific entry files instead of glob patterns.
Example: with-bun
examples/with-bun/*
Updated dependencies (arkenv, arktype, @types/bun, typescript); added module field; added start script; added tsconfig compiler options (verbatimModuleSyntax, noEmit, noImplicitOverride); expanded .gitignore; cleared .env.example.
Example: with-bun-react
examples/with-bun-react/*
Updated environment variables in .env files (replaced test/boolean vars with API_URL/DEBUG); modified build script (sourcemap, removed NODE_ENV define, improved logging); updated bun-env.d.ts module declarations; added reference directive; modified package.json scripts and dependencies; updated src/* files to reflect new env vars; removed .cursor/rules file.
Example: with-vite-react
examples/with-vite-react/*
Updated dependencies and replaced vite with rolldown-vite; updated environment variables in .env files (replaced VITE_TEST with VITE_MY_*); modified vite.config.ts Env schema; updated TypeScript configs (target ES2022/ES2023, verbatimModuleSyntax, erasableSyntaxOnly, baseUrl/paths); updated src/app.tsx, main.tsx, vite-env.d.ts, index.css to align with new Env schema; added packageManager declaration.
Documentation
examples/README.md
Replaced manual contribution workflow with synced examples table and instructions. Added sections for modifying/adding synced examples and maintaining manual examples separately.

Sequence Diagram

sequenceDiagram
    participant CLI as CLI Entry
    participant Script as sync-examples.js
    participant Catalog as pnpm-workspace.yaml
    participant Playgrounds as Playgrounds (arkenvExamples)
    participant Transformer as Transform Logic
    participant Examples as Examples Directory

    CLI->>Script: npm run sync:examples [--check]
    Script->>Catalog: Load catalog versions
    Catalog-->>Script: Parsed version map
    
    Script->>Playgrounds: Enumerate playgrounds with arkenvExamples
    
    loop For each playground + example config
        Playgrounds-->>Script: Playground files + metadata
        
        alt Check Mode
            Script->>Playgrounds: Read playground files
            Script->>Examples: Compare with existing examples
            Examples-->>Script: Current example files
            Script->>Transformer: Transform playground package.json
            Transformer-->>Script: Transformed for comparison
            Script->>Script: Detect differences (Missing/Modified/Extra)
            Script-->>Script: Accumulate changes
        else Sync Mode
            Script->>Transformer: Transform package.json<br/>(deps, workspace → published, packageManager)
            Transformer-->>Script: Transformed package.json
            Script->>Examples: Copy playground files recursively
            Script->>Examples: Write transformed package.json
            Examples-->>Script: Files written
            Script->>Examples: Create/update .gitignore
        end
    end
    
    alt Check Mode + Differences Detected
        Script-->>CLI: Exit code 1 + Report changes
    else Sync Mode
        Script-->>CLI: Exit code 0 + Success
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Key areas requiring attention:
    • bin/sync-examples.js: Complex transformation logic for workspace → published dependencies, package.json mutation, recursive file copying with exclusions, and check-mode diff comparison
    • Playground metadata declarations: Verify arkenvExamples entries match intended example outputs
    • Cross-example consistency: Ensure environment variable changes in examples/with-bun-react and examples/with-vite-react align with their source playgrounds and transformations applied correctly
    • GitHub Actions workflow: Confirm trigger paths and build/check steps execute correctly on PRs

Possibly related PRs

Poem

🐰 From playgrounds wild, examples bloom,
No duplication in our room!
One source of truth, no drift, no fuss,
Sync transforms workspace → published trust.
Playgrounds, examples—finally as one! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive While most changes directly support the sync infrastructure objective, multiple example files contain unrelated updates (e.g., .env files, tsconfig changes, CSS font-family modifications) that appear to be refactoring beyond the stated scope of implementing sync tooling. Clarify whether environment variable updates, TypeScript configuration changes, and styling updates in examples are intentional enhancements or should be separated into a distinct maintenance PR.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add playground-to-example sync infrastructure' accurately summarizes the main change: implementing a sync system to auto-generate examples from playgrounds, which is the core objective of the PR.
Linked Issues check ✅ Passed The PR successfully implements all core requirements from issue #460: playground metadata support via arkenvExamples [#460], sync script for transforming dependencies and copying files [#460], CI workflow for validation [#460], and documentation updates [#460].
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.

Comment @coderabbitai help to get the list of available commands and usage tips.

@vercel
Copy link

vercel bot commented Nov 29, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
arkenv Ready Ready Preview Comment Dec 9, 2025 7:35pm

Copilot AI and others added 2 commits November 29, 2025 20:49
Co-authored-by: yamcodes <2014360+yamcodes@users.noreply.github.com>
Co-authored-by: yamcodes <2014360+yamcodes@users.noreply.github.com>
Copilot AI changed the title [WIP] Consolidate playgrounds and examples for better maintenance Add playground-to-example sync infrastructure Nov 29, 2025
Copilot AI requested a review from yamcodes November 29, 2025 20:54
@github-actions github-actions bot added docs Improvements or additions to documentation github actions Pull requests that update GitHub Actions code example Issues or Pull Requests concerning at least one ArkEnv example. (Found in the `examples/` directory) labels Nov 29, 2025
@yamcodes yamcodes marked this pull request as ready for review November 29, 2025 21:11
@yamcodes
Copy link
Owner

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 29, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
examples/with-vite-react/package.json (1)

11-11: Add rimraf as a devDependency.

The clean script uses rimraf but the package is not declared in devDependencies. This will cause the script to fail when executed.

Apply this diff to add the missing dependency:

 	"devDependencies": {
 		"@julr/vite-plugin-validate-env": "2.2.0",
 		"@types/react": "^19.2.7",
 		"@types/react-dom": "^19.2.3",
 		"@vitejs/plugin-react": "^5.1.1",
 		"globals": "^16.5.0",
+		"rimraf": "^6.0.1",
 		"typescript": "^5.9.3",
 		"vite": "npm:rolldown-vite@latest",
 		"vite-tsconfig-paths": "^5.1.4"
 	},
🧹 Nitpick comments (6)
examples/with-vite-react/.env.development (1)

1-4: Env keys look good; consider satisfying dotenv-linter hints

The new variables and values align with typical Vite usage and the described Env schema. To avoid dotenv-linter noise (if you run it), you might:

  • Alphabetize the VITE_ keys (e.g., VITE_MY_BOOLEAN, VITE_MY_NUMBER, VITE_MY_VAR).
  • Add a trailing newline at the end of the file.

Both are purely stylistic and optional.

examples/with-vite-react/vite.config.ts (1)

22-22: Remove or conditionalize the debug console.log.

The console.log statement will execute during every config load in both development and production builds. Consider removing it or making it conditional on development mode.

-	console.log(`${env.VITE_MY_NUMBER} ${typeof env.VITE_MY_NUMBER}`);
+	if (mode === "development") {
+		console.log(`${env.VITE_MY_NUMBER} ${typeof env.VITE_MY_NUMBER}`);
+	}
examples/with-vite-react/src/app.tsx (1)

12-17: Consider security implications of rel attribute change.

Changed from noreferrer to noopener. While noopener prevents window.opener access, it allows the referrer header to be sent. For public documentation sites like vite.dev and react.dev, this is acceptable. However, be aware that the referrer header will now be included.

bin/sync-examples.js (3)

52-83: Custom YAML parsing is fragile but acceptable for this use case.

The line-by-line parsing works for pnpm's flat catalog structure, but it won't handle edge cases like multi-line values, comments on the same line as values, or nested structures. Consider using a YAML parser like js-yaml if the catalog format becomes more complex in the future.


88-106: Hardcoded package mapping requires manual updates.

When new workspace packages are added (e.g., a future @arkenv/next-plugin), this mapping must be updated manually. Consider dynamically discovering packages from the packages/ directory for better maintainability.

-function getWorkspacePackageVersion(packageName) {
-	// Map package names to their directories
-	const packageDirs = {
-		arkenv: join(ROOT_DIR, "packages", "arkenv"),
-		"@arkenv/vite-plugin": join(ROOT_DIR, "packages", "vite-plugin"),
-		"@arkenv/bun-plugin": join(ROOT_DIR, "packages", "bun-plugin"),
-	};
-
-	const packageDir = packageDirs[packageName];
+function getWorkspacePackageVersion(packageName) {
+	// Dynamically find package directory
+	const packagesDir = join(ROOT_DIR, "packages");
+	const packageDirs = readdirSync(packagesDir, { withFileTypes: true })
+		.filter((d) => d.isDirectory())
+		.map((d) => join(packagesDir, d.name));
+
+	const packageDir = packageDirs.find((dir) => {
+		const pkgPath = join(dir, "package.json");
+		if (!existsSync(pkgPath)) return false;
+		const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
+		return pkg.name === packageName;
+	});

117-124: Consider warning when workspace dependency cannot be resolved.

When getWorkspacePackageVersion returns null at line 118, the dependency is kept as workspace:* which won't work in standalone examples. A warning here would help developers catch configuration issues.

 		if (version === "workspace:*" || version.startsWith("workspace:")) {
 			const publishedVersion = getWorkspacePackageVersion(name);
 			if (publishedVersion) {
 				transformed[name] = `^${publishedVersion}`;
 			} else {
+				console.warn(`Warning: Could not resolve workspace dependency ${name}`);
 				// Keep as-is if we can't find the version
 				transformed[name] = version;
 			}

@yamcodes
Copy link
Owner

yamcodes commented Dec 1, 2025

@copilot all examples should be synced from the playgrounds. even the "basic" example, which should be synced through the "node playground", and the "basic-js" example, which we can sync via a new playground called "js-playground" which is like the node playground but in vanilla js (see basic-js example for setup)

Co-authored-by: yamcodes <2014360+yamcodes@users.noreply.github.com>
…rs, package.json transformation, and workspace dependency resolution.
@yamcodes
Copy link
Owner

yamcodes commented Dec 9, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 9, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@yamcodes
Copy link
Owner

yamcodes commented Dec 9, 2025

@coderabbitai do you approve?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 9, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# List all examples in the examples directory
echo "=== Examples in examples/ directory ==="
fd --type d --max-depth 1 . examples/ | sort

echo -e "\n=== Playgrounds in apps/playgrounds/ directory ==="
fd --type d --max-depth 1 . apps/playgrounds/ | sort

echo -e "\n=== Checking for arkenvExamples metadata in playgrounds ==="
fd --type f --max-depth 2 'package.json' apps/playgrounds/ --exec sh -c 'if grep -q "arkenvExamples" "$1"; then echo "$1:"; jq ".arkenvExamples" "$1"; fi' _ {}

echo -e "\n=== Checking if basic or basic-js examples exist ==="
ls -la examples/ | grep -E "basic|basic-js" || echo "No basic or basic-js examples found"

echo -e "\n=== Checking if node or js-playground playgrounds exist ==="
ls -la apps/playgrounds/ | grep -E "^d.*node|^d.*js-playground" || echo "No node or js-playground playgrounds found"

Length of output: 1230


🏁 Script executed:

#!/bin/bash
# Check the examples README to see which examples are manually maintained
echo "=== Checking examples/README.md for manually maintained examples ==="
cat examples/README.md

echo -e "\n=== Checking if with-standard-schema has a corresponding playground ==="
fd --type d 'standard-schema' apps/playgrounds/ || echo "No standard-schema playground found"

echo -e "\n=== Checking solid-start playground for arkenvExamples ==="
if [ -f "apps/playgrounds/solid-start/package.json" ]; then
  jq '.arkenvExamples // "No arkenvExamples found"' apps/playgrounds/solid-start/package.json
else
  echo "solid-start package.json not found"
fi

Length of output: 3879


🏁 Script executed:

#!/bin/bash
# Run the sync script in check mode to see if it catches issues
echo "=== Running sync:examples:check to verify sync status ==="
node bin/sync-examples.js --check

echo -e "\n=== Checking with-standard-schema example structure ==="
ls -la examples/with-standard-schema/ 2>/dev/null || echo "with-standard-schema directory not found"

echo -e "\n=== Checking if solid-start has example output ==="
ls -la examples/ | grep solid || echo "No solid-start example found"

Length of output: 887


@yamcodes, I've reviewed the PR implementation and verified that the sync infrastructure works correctly for the examples you mentioned. The basic and basic-js examples are properly configured to sync from the node and js playgrounds respectively. ✅

However, I've identified an inconsistency that needs to be addressed before I can approve:

Issue: with-standard-schema example is not synced

The with-standard-schema example exists in the examples/ directory but has no corresponding playground to sync from. This violates your requirement that "all examples should be synced from the playgrounds."

Current state:

  • ✅ Synced examples: basic, basic-js, with-bun, with-bun-react, with-vite-react
  • Not synced: with-standard-schema

Options:

  1. Create a new playground (e.g., apps/playgrounds/standard-schema) with arkenvExamples metadata pointing to with-standard-schema
  2. Remove the with-standard-schema example if it's no longer needed
  3. Document it explicitly as a manually-maintained exception (though this contradicts your stated goal)

Note: The solid-start playground exists but has no arkenvExamples metadata, which appears intentional (internal-only playground per the PR design).

Could you clarify how you'd like to handle the with-standard-schema example? Once that's resolved, I'll be happy to approve the PR.

… example dependencies, and clean up its gitignore and example environment file.
@yamcodes
Copy link
Owner

yamcodes commented Dec 9, 2025

@coderabbitai LGTY?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 9, 2025

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check current state of examples directory
echo "=== Current examples in examples/ directory ==="
fd --type d --max-depth 1 . examples/ | sort

echo -e "\n=== Current playgrounds with arkenvExamples metadata ==="
fd --type f --max-depth 2 'package.json' apps/playgrounds/ --exec sh -c 'if grep -q "arkenvExamples" "$1"; then echo "$1:"; jq -r ".arkenvExamples[] | \"  - \(.name) (packageManager: \(.packageManager))\"" "$1"; fi' _ {}

echo -e "\n=== Checking if with-standard-schema example still exists ==="
if [ -d "examples/with-standard-schema" ]; then
  echo "✗ with-standard-schema still exists"
  ls -la examples/with-standard-schema/
else
  echo "✓ with-standard-schema has been removed"
fi

echo -e "\n=== Running sync check to verify all examples are in sync ==="
node bin/sync-examples.js --check
echo "Exit code: $?"

Length of output: 1556


@yamcodes Yes, LGTM! 🎉

All my previous concerns have been addressed:

with-standard-schema now has a corresponding playground (apps/playgrounds/standard-schema) with proper sync configuration
basic example is synced from the node playground
basic-js example is synced from the new js playground
All 6 examples pass the sync check

The sync infrastructure is working perfectly—all examples are now generated from playgrounds as intended. Great work consolidating the codebase!

 />  />
(^.^ )
(")_(")
✅ Actions performed

Comments resolved and changes approved.

@yamcodes yamcodes merged commit 342e846 into main Dec 9, 2025
16 checks passed
@yamcodes yamcodes deleted the copilot/consolidate-playgrounds-examples branch December 9, 2025 19:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Improvements or additions to documentation example Issues or Pull Requests concerning at least one ArkEnv example. (Found in the `examples/` directory) github actions Pull requests that update GitHub Actions code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consolidate Playgrounds and Examples

2 participants