From 5ead38bf71ac6fb1e2ceeff360a6d712731dad46 Mon Sep 17 00:00:00 2001 From: Huakun Shen Date: Fri, 15 Nov 2024 18:15:17 -0500 Subject: [PATCH] Add extension templates, create-kunkun and cli package (#26) * chore: add extension templates * feat: add create-kunkun and cli package * fix: cli and create-kunkun package location * fix: cli package test * ci: run test for CI pipeline only on Linux The most important E2E test is run with docker, Linux anyways, no need to run on Mac and Windows --- .github/workflows/ci.yml | 1 + apps/cli/.gitignore | 175 ++++ apps/cli/CHANGELOG.md | 81 ++ apps/cli/README.md | 16 + apps/cli/__tests__/build-extension.test.ts | 80 ++ apps/cli/__tests__/verify.test.ts | 30 + apps/cli/build.ts | 24 + apps/cli/bun.lockb | Bin 0 -> 3124 bytes apps/cli/cli.ts | 50 ++ apps/cli/mod.ts | 9 + apps/cli/package.json | 44 + apps/cli/src/commands/build.ts | 12 + apps/cli/src/commands/index.ts | 2 + apps/cli/src/commands/verify.ts | 106 +++ apps/cli/src/constants.ts | 31 + apps/cli/src/docker/entrypoint.sh | 17 + apps/cli/src/logger.ts | 17 + apps/cli/src/types.ts | 9 + apps/cli/src/utils.ts | 179 ++++ apps/cli/tsconfig.json | 32 + apps/create-kunkun/.gitignore | 175 ++++ apps/create-kunkun/CHANGELOG.md | 116 +++ apps/create-kunkun/README.md | 16 + .../__tests__/create-template.test.ts | 42 + apps/create-kunkun/build.ts | 79 ++ apps/create-kunkun/bun.lockb | Bin 0 -> 3143 bytes apps/create-kunkun/index.ts | 193 +++++ apps/create-kunkun/package.json | 43 + apps/create-kunkun/rollup.config.js | 32 + apps/create-kunkun/scripts/postbuild.ts | 39 + apps/create-kunkun/src/__tests__/util.test.ts | 13 + apps/create-kunkun/src/constants.ts | 18 + apps/create-kunkun/src/patch.ts | 101 +++ apps/create-kunkun/src/utils.ts | 108 +++ apps/create-kunkun/tsconfig.json | 27 + pnpm-lock.yaml | 820 +++++++++++++++++- templates/index.html | 1 + templates/template-ext-next/.eslintrc.json | 3 + templates/template-ext-next/.gitignore | 37 + templates/template-ext-next/CHANGELOG.md | 8 + templates/template-ext-next/README.md | 69 ++ templates/template-ext-next/next.config.mjs | 7 + templates/template-ext-next/package.json | 62 ++ .../template-ext-next/postcss.config.mjs | 8 + templates/template-ext-next/public/next.svg | 1 + templates/template-ext-next/public/vercel.svg | 1 + .../template-ext-next/src/app/about/page.tsx | 10 + .../template-ext-next/src/app/favicon.ico | Bin 0 -> 25931 bytes .../template-ext-next/src/app/globals.css | 6 + .../template-ext-next/src/app/layout.tsx | 22 + templates/template-ext-next/src/app/page.tsx | 11 + .../src/components/about.tsx | 16 + .../template-ext-next/src/components/main.tsx | 17 + .../template-ext-next/tailwind.config.ts | 20 + templates/template-ext-next/tsconfig.json | 27 + templates/template-ext-nuxt/.gitignore | 25 + templates/template-ext-nuxt/CHANGELOG.md | 15 + templates/template-ext-nuxt/README.md | 69 ++ templates/template-ext-nuxt/app.vue | 3 + templates/template-ext-nuxt/nuxt.config.ts | 16 + templates/template-ext-nuxt/package.json | 59 ++ templates/template-ext-nuxt/pages/about.vue | 11 + templates/template-ext-nuxt/pages/index.vue | 50 ++ .../template-ext-nuxt/public/favicon.ico | Bin 0 -> 4286 bytes .../template-ext-nuxt/server/tsconfig.json | 3 + templates/template-ext-nuxt/tsconfig.json | 4 + templates/template-ext-react/.eslintrc.cjs | 18 + templates/template-ext-react/.gitignore | 26 + templates/template-ext-react/CHANGELOG.md | 15 + templates/template-ext-react/README.md | 67 ++ templates/template-ext-react/index.html | 13 + templates/template-ext-react/package.json | 63 ++ .../template-ext-react/postcss.config.js | 6 + templates/template-ext-react/public/vite.svg | 1 + templates/template-ext-react/src/App.tsx | 156 ++++ templates/template-ext-react/src/index.css | 6 + templates/template-ext-react/src/main.tsx | 10 + .../template-ext-react/src/vite-env.d.ts | 1 + .../template-ext-react/tailwind.config.js | 12 + .../template-ext-react/tsconfig.app.json | 27 + templates/template-ext-react/tsconfig.json | 11 + .../template-ext-react/tsconfig.node.json | 13 + templates/template-ext-react/vite.config.ts | 7 + templates/template-ext-svelte/.gitignore | 26 + .../.vscode/extensions.json | 3 + templates/template-ext-svelte/CHANGELOG.md | 15 + templates/template-ext-svelte/README.md | 67 ++ templates/template-ext-svelte/components.json | 14 + templates/template-ext-svelte/index.html | 13 + templates/template-ext-svelte/package.json | 65 ++ .../template-ext-svelte/postcss.config.js | 6 + templates/template-ext-svelte/public/vite.svg | 1 + templates/template-ext-svelte/src/App.svelte | 98 +++ templates/template-ext-svelte/src/app.css | 80 ++ .../template-ext-svelte/src/assets/svelte.svg | 1 + .../src/lib/Counter.svelte | 10 + .../src/lib/components/ThemeCustomizer.svelte | 20 + .../template-ext-svelte/src/lib/utils.ts | 62 ++ templates/template-ext-svelte/src/main.ts | 9 + .../template-ext-svelte/src/vite-env.d.ts | 2 + .../template-ext-svelte/svelte.config.js | 7 + .../template-ext-svelte/tailwind.config.ts | 67 ++ templates/template-ext-svelte/tsconfig.json | 38 + .../template-ext-svelte/tsconfig.node.json | 12 + templates/template-ext-svelte/vite.config.ts | 13 + templates/template-ext-sveltekit/.gitignore | 23 + templates/template-ext-sveltekit/.npmrc | 1 + .../template-ext-sveltekit/.prettierignore | 4 + templates/template-ext-sveltekit/.prettierrc | 15 + templates/template-ext-sveltekit/CHANGELOG.md | 15 + templates/template-ext-sveltekit/README.md | 69 ++ .../template-ext-sveltekit/components.json | 14 + .../template-ext-sveltekit/eslint.config.js | 33 + templates/template-ext-sveltekit/package.json | 84 ++ .../template-ext-sveltekit/postcss.config.js | 6 + templates/template-ext-sveltekit/src/app.css | 80 ++ templates/template-ext-sveltekit/src/app.d.ts | 13 + templates/template-ext-sveltekit/src/app.html | 12 + .../src/lib/components/ThemeCustomizer.svelte | 20 + .../template-ext-sveltekit/src/lib/index.ts | 1 + .../template-ext-sveltekit/src/lib/utils.ts | 62 ++ .../src/routes/+layout.svelte | 19 + .../src/routes/+layout.ts | 2 + .../src/routes/+page.svelte | 102 +++ .../src/routes/about/+page.svelte | 15 + .../template-ext-sveltekit/static/favicon.png | Bin 0 -> 1571 bytes .../template-ext-sveltekit/svelte.config.js | 21 + .../template-ext-sveltekit/tailwind.config.ts | 67 ++ .../template-ext-sveltekit/tsconfig.json | 19 + .../template-ext-sveltekit/vite.config.ts | 6 + templates/template-ext-vue/.gitignore | 26 + .../template-ext-vue/.vscode/extensions.json | 3 + templates/template-ext-vue/CHANGELOG.md | 15 + templates/template-ext-vue/README.md | 67 ++ templates/template-ext-vue/components.json | 17 + templates/template-ext-vue/index.html | 13 + templates/template-ext-vue/package.json | 64 ++ templates/template-ext-vue/postcss.config.js | 6 + templates/template-ext-vue/public/vite.svg | 1 + templates/template-ext-vue/src/App.vue | 87 ++ templates/template-ext-vue/src/assets/vue.svg | 1 + templates/template-ext-vue/src/index.css | 80 ++ templates/template-ext-vue/src/lib/utils.ts | 6 + templates/template-ext-vue/src/main.ts | 5 + templates/template-ext-vue/src/vite-env.d.ts | 1 + templates/template-ext-vue/tailwind.config.js | 93 ++ templates/template-ext-vue/tsconfig.app.json | 31 + templates/template-ext-vue/tsconfig.json | 11 + templates/template-ext-vue/tsconfig.node.json | 13 + templates/template-ext-vue/vite.config.ts | 19 + templates/template-ext-worker/.gitignore | 177 ++++ templates/template-ext-worker/README.md | 125 +++ templates/template-ext-worker/build.ts | 30 + templates/template-ext-worker/package.json | 47 + templates/template-ext-worker/src/i18n/en.ts | 5 + .../template-ext-worker/src/i18n/index.ts | 20 + templates/template-ext-worker/src/i18n/zh.ts | 5 + templates/template-ext-worker/src/index.ts | 126 +++ templates/template-ext-worker/tsconfig.json | 27 + 159 files changed, 6214 insertions(+), 9 deletions(-) create mode 100644 apps/cli/.gitignore create mode 100644 apps/cli/CHANGELOG.md create mode 100644 apps/cli/README.md create mode 100644 apps/cli/__tests__/build-extension.test.ts create mode 100644 apps/cli/__tests__/verify.test.ts create mode 100644 apps/cli/build.ts create mode 100755 apps/cli/bun.lockb create mode 100644 apps/cli/cli.ts create mode 100644 apps/cli/mod.ts create mode 100644 apps/cli/package.json create mode 100644 apps/cli/src/commands/build.ts create mode 100644 apps/cli/src/commands/index.ts create mode 100644 apps/cli/src/commands/verify.ts create mode 100644 apps/cli/src/constants.ts create mode 100755 apps/cli/src/docker/entrypoint.sh create mode 100644 apps/cli/src/logger.ts create mode 100644 apps/cli/src/types.ts create mode 100644 apps/cli/src/utils.ts create mode 100644 apps/cli/tsconfig.json create mode 100644 apps/create-kunkun/.gitignore create mode 100644 apps/create-kunkun/CHANGELOG.md create mode 100644 apps/create-kunkun/README.md create mode 100644 apps/create-kunkun/__tests__/create-template.test.ts create mode 100644 apps/create-kunkun/build.ts create mode 100755 apps/create-kunkun/bun.lockb create mode 100644 apps/create-kunkun/index.ts create mode 100644 apps/create-kunkun/package.json create mode 100644 apps/create-kunkun/rollup.config.js create mode 100644 apps/create-kunkun/scripts/postbuild.ts create mode 100644 apps/create-kunkun/src/__tests__/util.test.ts create mode 100644 apps/create-kunkun/src/constants.ts create mode 100644 apps/create-kunkun/src/patch.ts create mode 100644 apps/create-kunkun/src/utils.ts create mode 100644 apps/create-kunkun/tsconfig.json create mode 100644 templates/index.html create mode 100644 templates/template-ext-next/.eslintrc.json create mode 100644 templates/template-ext-next/.gitignore create mode 100644 templates/template-ext-next/CHANGELOG.md create mode 100644 templates/template-ext-next/README.md create mode 100644 templates/template-ext-next/next.config.mjs create mode 100644 templates/template-ext-next/package.json create mode 100644 templates/template-ext-next/postcss.config.mjs create mode 100644 templates/template-ext-next/public/next.svg create mode 100644 templates/template-ext-next/public/vercel.svg create mode 100644 templates/template-ext-next/src/app/about/page.tsx create mode 100644 templates/template-ext-next/src/app/favicon.ico create mode 100644 templates/template-ext-next/src/app/globals.css create mode 100644 templates/template-ext-next/src/app/layout.tsx create mode 100644 templates/template-ext-next/src/app/page.tsx create mode 100644 templates/template-ext-next/src/components/about.tsx create mode 100644 templates/template-ext-next/src/components/main.tsx create mode 100644 templates/template-ext-next/tailwind.config.ts create mode 100644 templates/template-ext-next/tsconfig.json create mode 100644 templates/template-ext-nuxt/.gitignore create mode 100644 templates/template-ext-nuxt/CHANGELOG.md create mode 100644 templates/template-ext-nuxt/README.md create mode 100644 templates/template-ext-nuxt/app.vue create mode 100644 templates/template-ext-nuxt/nuxt.config.ts create mode 100644 templates/template-ext-nuxt/package.json create mode 100644 templates/template-ext-nuxt/pages/about.vue create mode 100644 templates/template-ext-nuxt/pages/index.vue create mode 100644 templates/template-ext-nuxt/public/favicon.ico create mode 100644 templates/template-ext-nuxt/server/tsconfig.json create mode 100644 templates/template-ext-nuxt/tsconfig.json create mode 100644 templates/template-ext-react/.eslintrc.cjs create mode 100644 templates/template-ext-react/.gitignore create mode 100644 templates/template-ext-react/CHANGELOG.md create mode 100644 templates/template-ext-react/README.md create mode 100644 templates/template-ext-react/index.html create mode 100644 templates/template-ext-react/package.json create mode 100644 templates/template-ext-react/postcss.config.js create mode 100644 templates/template-ext-react/public/vite.svg create mode 100644 templates/template-ext-react/src/App.tsx create mode 100644 templates/template-ext-react/src/index.css create mode 100644 templates/template-ext-react/src/main.tsx create mode 100644 templates/template-ext-react/src/vite-env.d.ts create mode 100644 templates/template-ext-react/tailwind.config.js create mode 100644 templates/template-ext-react/tsconfig.app.json create mode 100644 templates/template-ext-react/tsconfig.json create mode 100644 templates/template-ext-react/tsconfig.node.json create mode 100644 templates/template-ext-react/vite.config.ts create mode 100644 templates/template-ext-svelte/.gitignore create mode 100644 templates/template-ext-svelte/.vscode/extensions.json create mode 100644 templates/template-ext-svelte/CHANGELOG.md create mode 100644 templates/template-ext-svelte/README.md create mode 100644 templates/template-ext-svelte/components.json create mode 100644 templates/template-ext-svelte/index.html create mode 100644 templates/template-ext-svelte/package.json create mode 100644 templates/template-ext-svelte/postcss.config.js create mode 100644 templates/template-ext-svelte/public/vite.svg create mode 100644 templates/template-ext-svelte/src/App.svelte create mode 100644 templates/template-ext-svelte/src/app.css create mode 100644 templates/template-ext-svelte/src/assets/svelte.svg create mode 100644 templates/template-ext-svelte/src/lib/Counter.svelte create mode 100644 templates/template-ext-svelte/src/lib/components/ThemeCustomizer.svelte create mode 100644 templates/template-ext-svelte/src/lib/utils.ts create mode 100644 templates/template-ext-svelte/src/main.ts create mode 100644 templates/template-ext-svelte/src/vite-env.d.ts create mode 100644 templates/template-ext-svelte/svelte.config.js create mode 100644 templates/template-ext-svelte/tailwind.config.ts create mode 100644 templates/template-ext-svelte/tsconfig.json create mode 100644 templates/template-ext-svelte/tsconfig.node.json create mode 100644 templates/template-ext-svelte/vite.config.ts create mode 100644 templates/template-ext-sveltekit/.gitignore create mode 100644 templates/template-ext-sveltekit/.npmrc create mode 100644 templates/template-ext-sveltekit/.prettierignore create mode 100644 templates/template-ext-sveltekit/.prettierrc create mode 100644 templates/template-ext-sveltekit/CHANGELOG.md create mode 100644 templates/template-ext-sveltekit/README.md create mode 100644 templates/template-ext-sveltekit/components.json create mode 100644 templates/template-ext-sveltekit/eslint.config.js create mode 100644 templates/template-ext-sveltekit/package.json create mode 100644 templates/template-ext-sveltekit/postcss.config.js create mode 100644 templates/template-ext-sveltekit/src/app.css create mode 100644 templates/template-ext-sveltekit/src/app.d.ts create mode 100644 templates/template-ext-sveltekit/src/app.html create mode 100644 templates/template-ext-sveltekit/src/lib/components/ThemeCustomizer.svelte create mode 100644 templates/template-ext-sveltekit/src/lib/index.ts create mode 100644 templates/template-ext-sveltekit/src/lib/utils.ts create mode 100644 templates/template-ext-sveltekit/src/routes/+layout.svelte create mode 100644 templates/template-ext-sveltekit/src/routes/+layout.ts create mode 100644 templates/template-ext-sveltekit/src/routes/+page.svelte create mode 100644 templates/template-ext-sveltekit/src/routes/about/+page.svelte create mode 100644 templates/template-ext-sveltekit/static/favicon.png create mode 100644 templates/template-ext-sveltekit/svelte.config.js create mode 100644 templates/template-ext-sveltekit/tailwind.config.ts create mode 100644 templates/template-ext-sveltekit/tsconfig.json create mode 100644 templates/template-ext-sveltekit/vite.config.ts create mode 100644 templates/template-ext-vue/.gitignore create mode 100644 templates/template-ext-vue/.vscode/extensions.json create mode 100644 templates/template-ext-vue/CHANGELOG.md create mode 100644 templates/template-ext-vue/README.md create mode 100644 templates/template-ext-vue/components.json create mode 100644 templates/template-ext-vue/index.html create mode 100644 templates/template-ext-vue/package.json create mode 100644 templates/template-ext-vue/postcss.config.js create mode 100644 templates/template-ext-vue/public/vite.svg create mode 100644 templates/template-ext-vue/src/App.vue create mode 100644 templates/template-ext-vue/src/assets/vue.svg create mode 100644 templates/template-ext-vue/src/index.css create mode 100644 templates/template-ext-vue/src/lib/utils.ts create mode 100644 templates/template-ext-vue/src/main.ts create mode 100644 templates/template-ext-vue/src/vite-env.d.ts create mode 100644 templates/template-ext-vue/tailwind.config.js create mode 100644 templates/template-ext-vue/tsconfig.app.json create mode 100644 templates/template-ext-vue/tsconfig.json create mode 100644 templates/template-ext-vue/tsconfig.node.json create mode 100644 templates/template-ext-vue/vite.config.ts create mode 100644 templates/template-ext-worker/.gitignore create mode 100644 templates/template-ext-worker/README.md create mode 100644 templates/template-ext-worker/build.ts create mode 100644 templates/template-ext-worker/package.json create mode 100644 templates/template-ext-worker/src/i18n/en.ts create mode 100644 templates/template-ext-worker/src/i18n/index.ts create mode 100644 templates/template-ext-worker/src/i18n/zh.ts create mode 100644 templates/template-ext-worker/src/index.ts create mode 100644 templates/template-ext-worker/tsconfig.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8a2fa15..35c945a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,4 +36,5 @@ jobs: - name: Build run: pnpm build - name: Test + if: matrix.os == 'ubuntu-24.04' run: pnpm test diff --git a/apps/cli/.gitignore b/apps/cli/.gitignore new file mode 100644 index 0000000..9b1ee42 --- /dev/null +++ b/apps/cli/.gitignore @@ -0,0 +1,175 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/apps/cli/CHANGELOG.md b/apps/cli/CHANGELOG.md new file mode 100644 index 0000000..4cab5eb --- /dev/null +++ b/apps/cli/CHANGELOG.md @@ -0,0 +1,81 @@ +# kksh + +## 0.0.22 + +### Patch Changes + +- Updated dependencies + - @kksh/api@0.0.23 + +## 0.0.21 + +### Patch Changes + +- Updated dependencies + - @kksh/api@0.0.22 + +## 0.0.20 + +### Patch Changes + +- Re-enable server-based dev extension refresh. Deep Link forces switch focus to kunkun +- Updated dependencies + - @kksh/api@0.0.21 + +## 0.0.19 + +### Patch Changes + +- Fix Some Windows incompatibilities +- Updated dependencies + - @kksh/api@0.0.20 + +## 0.0.12 + +### Patch Changes + +- Updated dependencies [9fa3d8e] +- Updated dependencies [0a1ab7c] +- Updated dependencies [b48d53b] + - @kksh/api@0.0.20 + +## 0.0.9 + +### Patch Changes + +- Updated dependencies + - @kksh/api@0.0.10 + +## 0.0.8 + +### Patch Changes + +- Updated dependencies + - @kksh/api@0.0.9 + +## 0.0.7 + +### Patch Changes + +- Add publish mode to verify command, will exit with 1 when invalid. + +## 0.0.6 + +### Patch Changes + +- Updated dependencies + - @kksh/api@0.0.6 + +## 0.0.5 + +### Patch Changes + +- Updated dependencies [ec47e1e] + - @kksh/api@0.0.5 + +## 0.0.4 + +### Patch Changes + +- Updated dependencies + - @kksh/api@0.0.4 diff --git a/apps/cli/README.md b/apps/cli/README.md new file mode 100644 index 0000000..6006a9a --- /dev/null +++ b/apps/cli/README.md @@ -0,0 +1,16 @@ +# kksh + +This is a CLI tool for developers to develop Kunkun extensions. + +## Usage + +```bash +# Create an extension template first +npm init kunkun@latest + +# You can verify the extension manifest, this expect `npm run build` is done already and all generated artifacts listed in manifest is present. +npx kksh verify + +# Build extension with docker, simulate how Kunkun's CI builds the extension +npx kksh build +``` diff --git a/apps/cli/__tests__/build-extension.test.ts b/apps/cli/__tests__/build-extension.test.ts new file mode 100644 index 0000000..7edba83 --- /dev/null +++ b/apps/cli/__tests__/build-extension.test.ts @@ -0,0 +1,80 @@ +import os from "os" +import path from "path" +import { getRootDir } from "@/constants" +import type { BuildResult } from "@/types" +import { buildWithDockerAndValidate } from "@/utils" +import { $ } from "bun" +import { afterAll, expect, test } from "bun:test" +import fs from "fs-extra" +import { verifyCmd } from "../src/commands/verify" + +const rootDir = getRootDir() +const createKKDir = path.join(rootDir, "../create-kunkun") + +const createKKDistDir = path.join(createKKDir, "dist") +const createKKIndexjsPath = path.join(createKKDistDir, "index.mjs") +const testDir = path.join(os.tmpdir(), "kunkun-cli-test") +console.log("Test Dir: ", testDir) +const templateNames = ["react", "vue", "nuxt", "svelte", "sveltekit", "next", "template"] + +fs.rmdirSync(testDir, { recursive: true }) +fs.mkdirpSync(testDir) +const testTemplateDirs: string[] = [] +await Promise.all( + templateNames.map(async (templateName) => { + const folderName = `${templateName}-ext` + await $`node ${createKKIndexjsPath} --outdir ${testDir} --name ${folderName} --template ${templateName}` + const templateDir = path.join(testDir, folderName) + console.log("templateDir", templateDir) + await $`pnpm install`.cwd(templateDir).quiet() + await $`pnpm build`.cwd(templateDir).quiet() + testTemplateDirs.push(templateDir) + }) +) + +test("Build And Verify", async () => { + for (const templateDir of testTemplateDirs) { + expect(verifyCmd(templateDir, false)).toBeTrue() + } +}) + +const testDirDocker = path.join(os.tmpdir(), "kunkun-cli-test-docker") +fs.rmdirSync(testDirDocker, { recursive: true }) +fs.mkdirpSync(testDirDocker) + +const templateData: Record = {} + +await Promise.all( + templateNames.map(async (templateName) => { + const folderName = `${templateName}-ext` + await $`node ${createKKIndexjsPath} --outdir ${testDirDocker} --name ${folderName} --template ${templateName}` + const templateDir = path.join(testDirDocker, folderName) + console.log("templateDir:", templateDir) + + const buildResult = await buildWithDockerAndValidate(templateDir) + templateData[templateName] = { + dir: templateDir, + buildResult + } + }) +) +console.log(templateData) + +test("Template Exist", () => { + Object.entries(templateData).forEach(async ([templateName, { dir }]) => { + console.log("Expect dir exist: ", dir) + expect(fs.existsSync(dir)).toBeTrue() + }) +}) + +test("Build Result Tarball Exist", () => { + Object.entries(templateData).forEach(async ([templateName, { buildResult, dir }]) => { + const expectedTarballPath = path.join(dir, buildResult.tarballFilename) + expect(fs.existsSync(expectedTarballPath)).toBeTrue() + }) +}) + +afterAll(() => { + fs.rmdirSync(testDir, { recursive: true }) + fs.rmdirSync(testDirDocker, { recursive: true }) +}) diff --git a/apps/cli/__tests__/verify.test.ts b/apps/cli/__tests__/verify.test.ts new file mode 100644 index 0000000..d70b73c --- /dev/null +++ b/apps/cli/__tests__/verify.test.ts @@ -0,0 +1,30 @@ +import { expect, test } from "bun:test" +import path from "path" +import { getRootDir } from "@/constants" +import fs from "fs-extra" +import { verifyCmd } from "../src/commands/verify" + +const rootDir = getRootDir() +const extensionsDir = path.join(rootDir, "../../packages/extensions") +const templatesDir = path.join(rootDir, "../../templates") + +const extsPaths = fs + .readdirSync(extensionsDir) + .map((extensionName) => path.join(extensionsDir, extensionName)) + .filter((extPath) => fs.statSync(extPath).isDirectory()) +const templatesPaths = fs + .readdirSync(templatesDir) + .map((templateName) => path.join(templatesDir, templateName)) + .filter((extPath) => fs.statSync(extPath).isDirectory()) + +test("Verify Extensions", () => { + for (const extPath of extsPaths) { + expect(verifyCmd(extPath, false)).toBeTrue() + } +}) + +// test("Verify Templates", () => { +// for (const templatePath of templatesPaths) { +// expect(verifyCmd(templatePath, false)).toBeTrue() +// } +// }) diff --git a/apps/cli/build.ts b/apps/cli/build.ts new file mode 100644 index 0000000..0ab66d0 --- /dev/null +++ b/apps/cli/build.ts @@ -0,0 +1,24 @@ +import { $ } from "bun" +import fs from "fs-extra" + +process.env.NODE_ENV = "production" + +if (Bun.env.NODE_ENV !== "production") { + console.error("This script should be run in production mode. Set NODE_ENV=production.") + process.exit(1) +} + +await $`rm -rf dist` +// building with bun doesn't work with debug +fs.mkdirSync("./dist") +process.env.NODE_ENV = "production" +await Bun.build({ + entrypoints: ["./cli.ts"], + outdir: "./dist", + target: "node", + // minify: true, + format: "esm" +}) + +// await $`bun build --target node cli.ts > dist/cli.js` +fs.cpSync("./src/docker", "./dist/docker", { recursive: true }) diff --git a/apps/cli/bun.lockb b/apps/cli/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..56bb31cc4e2ccff86b0a258c0b2ca20dc81f6221 GIT binary patch literal 3124 zcmY#Z)GsYA(of3F(@)JSQ%EY!;{sycoc!eMw9K4T-L(9o+{6;yG6OCq1_lP+m76wh zott!HgVKTQnr^)*li%F)lWSTowKP=$}Dgpv_2*tpGMmIqD*)RnR$vK%| zF)knp#0{Q6niEJ*1JXdl8g2t=9w6Nd6$esabx&6)$s|17s(gw~bFa5S1R8H zUMo%1kLp$1YHP`vZ*%Nb;H=$?PtT~FpLHp7zG{Pxc57-9Bgg;>04NkdKm>?Qf$jpC z2f}PXObmeO4+1K-M$#`xjAo!3nEr)8wiQr6$Q{7&Vc-K|VgOA4bD&~Jpng^$Ed|6N zniv4nuL1P52~a;MhCzCv5l_$vnEni)ejBL$ApI-^bs`ji%m87a5e&>wHpmPR9~KWF z8pMYuG$5NiegF9XfefN10Gh|h05KQYXqFu_xO&aQFJy`dlodN0=BT-@eE!ZnRqr_W zX-S*NbB9V+T;b-Op0#&z+R;kg?Pc1$e9Im*co>N}HOa+)_@tI&fMhN#oI%cJN$>Al z7A{o&O>*_NoV6dmTrlda(9tvPugVT$R#&OM|6G`Lp@;XDrWZRhlfQ0fI4Ev?W5(*q zp3jdq9ID~IHZNZb$y`u)!vM?1lZT&Q$xN^>JyxOZ$aHLi|E{2oSM1HZQZo{JL}uM7 zS#rqiLUFy_>XHn_K!c-<6Au^L_0PyOVYZvbT3#5XJqyWP6FOr^e^E!L`PoTL2 z1z_P0qsei{06L!>=MI#f4bZaa2$bfsDK1LZ%gie-NzBR7gVnftAw{XFb_zxY3dNaK zsrhL-3MLAPIhpBs`Dvg!5(NJJhX4>8lny^Y4dj4Uw_G-*#zuBPeK<@2#TTf20Oe&+ zyo2(H1ypYck!HZkSCBa%{V=mYbO+Q7J77f)0$euHMrN_47C>K_!QEtt?kh;W4y&gH z7~@R!4D<}ZqTm`IR?~AZKU2onPAbQ1@Ra|2h@ir;66o;(&E(IveY6y z*NT$VqP)bM;F6-uymUJSLqs50zyqPAI2ml_f!`uai>6Ev0X4`#V*K|O+q{!4+*MLs zoLU4lE%vfPV^Es+j~HMig4#tb7N%zyEBOBXfVyA~v?2zk0bqC=;tcMR%7WD54C@Co@w$j%xN=?r!E-9+i%PYvuD%Q&{O4mmy($@tk)dhvLnO;eH6^H{i z0grCD8mMNldax0O<{&emrk58(8F~3Bsd$V-QUx>+q*xachlVB~(@OJFGLth2Itp1C z&=_Q{E;!;%L52}_MtLz%cX_ccIDr|0g9IFRkodx*9}xvW0}vdb<)(UIk3bXIprZf= CnMEc5 literal 0 HcmV?d00001 diff --git a/apps/cli/cli.ts b/apps/cli/cli.ts new file mode 100644 index 0000000..8a9f196 --- /dev/null +++ b/apps/cli/cli.ts @@ -0,0 +1,50 @@ +#!/usr/bin/env node +import fs from "fs" +import path from "path" +import { buildCmd, verifyCmd } from "@/commands" +import { getDockerFolder, NODE_ENV } from "@/constants" +import logger from "@/logger" +import { program } from "commander" +import { version } from "./package.json" + +const cwd = process.cwd() +console.log("Environment:", NODE_ENV) + +program.name("Kunkun CLI").description("CLI for Kunkun Extension Development").version(version) + +function computeProjectDir(projectPath: string | undefined) { + if (!projectPath) { + projectPath = cwd + } else if (fs.existsSync(projectPath)) { + projectPath = path.resolve(projectPath) + } else if (fs.existsSync(path.join(cwd, projectPath))) { + projectPath = path.join(cwd, projectPath) + } else { + logger.error("Invalid project path") + process.exit(1) + } + return projectPath +} + +program + .command("verify [project_path]") + .description("Verify the validity of a Kunkun extension") + .option("-b, --batch", "Batch mode", false) + .option("-p, --publish", "Publish Mode. Will exit with 1 if invalid", false) + .action((projectPath: string | undefined, opts: { batch: boolean; publish: boolean }) => { + logger.info("cwd:", cwd) + const valid = verifyCmd(computeProjectDir(projectPath), opts.batch) + if (opts.publish && !valid) { + process.exit(1) + } + }) + +program + .command("build [project_path]") + .description("Build extension with docker and validate (You must have docker installed)") + .action((projectPath: string | undefined) => { + logger.info("cwd:", cwd) + buildCmd(computeProjectDir(projectPath)) + }) + +program.parse() diff --git a/apps/cli/mod.ts b/apps/cli/mod.ts new file mode 100644 index 0000000..69d98b6 --- /dev/null +++ b/apps/cli/mod.ts @@ -0,0 +1,9 @@ +export { buildWithDocker, buildWithDockerAndValidate } from "@/utils" +export type { BuildResult } from "@/types" +export { + verifyCustomUiCommand, + verifyTemplateUiCommand, + verifySingleProject, + verifyCmd +} from "@/commands/verify" +export { buildCmd } from "@/commands/build" diff --git a/apps/cli/package.json b/apps/cli/package.json new file mode 100644 index 0000000..66ad8d1 --- /dev/null +++ b/apps/cli/package.json @@ -0,0 +1,44 @@ +{ + "name": "kksh", + "module": "dist/cli.js", + "version": "0.0.23", + "type": "module", + "bin": { + "kksh": "./dist/cli.js", + "docker-entrypoint.sh": "./dist/docker/entrypoint.sh" + }, + "author": "Huakun", + "scripts": { + "build": "bun build.ts", + "test": "cross-env NODE_ENV=test bun test --coverage" + }, + "devDependencies": { + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-replace": "^5.0.7", + "@rollup/plugin-terser": "^0.4.4", + "@rollup/plugin-typescript": "^11.1.6", + "@types/bun": "latest", + "@types/debug": "^4.1.12", + "@types/fs-extra": "^11.0.4", + "cross-env": "^7.0.3", + "rollup": "^4.24.0", + "rollup-plugin-visualizer": "^5.12.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "@inquirer/prompts": "^5.2.1", + "@kksh/api": "workspace:*", + "chalk": "^5.3.0", + "commander": "^12.1.0", + "console-table-printer": "^2.12.1", + "debug": "^4.3.6", + "fs-extra": "^11.2.0", + "inquirer": "^10.1.2", + "valibot": "^0.40.0" + }, + "files": [ + "dist" + ] +} diff --git a/apps/cli/src/commands/build.ts b/apps/cli/src/commands/build.ts new file mode 100644 index 0000000..22436a9 --- /dev/null +++ b/apps/cli/src/commands/build.ts @@ -0,0 +1,12 @@ +import { getRootDir } from "@/constants" +import { buildWithDockerAndValidate } from "@/utils" + +export async function buildCmd(projectPath: string) { + const rootDir = getRootDir() + console.log("rootDir: ", rootDir) + + const buildResult = await buildWithDockerAndValidate(projectPath) + console.log(buildResult) +} + +export default buildCmd diff --git a/apps/cli/src/commands/index.ts b/apps/cli/src/commands/index.ts new file mode 100644 index 0000000..ac20cd7 --- /dev/null +++ b/apps/cli/src/commands/index.ts @@ -0,0 +1,2 @@ +export { default as verifyCmd } from "./verify" +export { default as buildCmd } from "./build" diff --git a/apps/cli/src/commands/verify.ts b/apps/cli/src/commands/verify.ts new file mode 100644 index 0000000..d6d1f6b --- /dev/null +++ b/apps/cli/src/commands/verify.ts @@ -0,0 +1,106 @@ +import path from "path" +import { NODE_ENV } from "@/constants" +import logger from "@/logger" +import { CustomUiCmd, ExtPackageJson, TemplateUiCmd } from "@kksh/api/models" +import { printTable } from "console-table-printer" +import fs from "fs-extra" +import * as v from "valibot" + +export function verifyCustomUiCommand(projectRoot: string, cmd: CustomUiCmd): boolean { + if (!cmd.main.startsWith("http")) { + const mainPath = path.join(projectRoot, cmd.dist, cmd.main) + if ( + !( + fs.existsSync(mainPath) || + fs.existsSync(mainPath + ".html") || + fs.existsSync(path.join(mainPath, "index.html")) + ) + ) { + logger.error(`main file not found at '${mainPath}' for command ${cmd.name}`) + return false + } + } + return true +} + +export function verifyTemplateUiCommand(projectRoot: string, cmd: TemplateUiCmd): boolean { + const mainPath = path.join(projectRoot, cmd.main) + if (!fs.existsSync(mainPath)) { + logger.error(`main file not found at ${mainPath} for command ${cmd.name}`) + return false + } + return true +} + +export function verifySingleProject(projectPath: string): boolean { + logger.info(`Verifying project at ${projectPath}`) + const pkgJsonPath = path.join(projectPath, "package.json") + if (!fs.existsSync(pkgJsonPath)) { + logger.error(`package.json not found at [${pkgJsonPath}]`) + return false + } + const pkgJson = fs.readJSONSync(pkgJsonPath) + const result = v.safeParse(ExtPackageJson, pkgJson) + + if (!result.success) { + logger.error("package.json is invalid, see issues below:") + console.error(v.flatten(result.issues)) + return false + } + const pkg = result.output + logger.info(`package.json is valid`) + logger.info(`name`, pkg.name) + logger.info(`version`, pkg.version) + logger.info(`identifier`, pkg.kunkun.identifier) + if (pkg.files.length === 0) { + logger.warn( + `"files" field is empty, it is recommended to include only the necessary files, e.g. dist` + ) + } + // check if kunkun extension name is the same as the folder name + const folderName = path.basename(projectPath) + + if (NODE_ENV === "test") { + // if (pkg.kunkun.identifier === "{{projectName}}") { + console.log("Patching project name from {{projectName}} to", folderName) + pkg.kunkun.identifier = folderName + // } + } + if (pkg.kunkun.identifier !== folderName) { + logger.error( + `Extension package name at [pkg.kunkun.identifier](${pkg.kunkun.identifier}) is not the same as the folder name [${folderName}], please fix it` + ) + return false + } + for (const cmd of pkg.kunkun.customUiCmds) { + if (!verifyCustomUiCommand(projectPath, cmd)) { + return false + } + } + for (const cmd of pkg.kunkun.templateUiCmds) { + if (!verifyTemplateUiCommand(projectPath, cmd)) { + return false + } + } + return true +} + +export function verifyCmd(projectPath: string, batch: boolean): boolean { + let success = true + if (!batch) { + success = verifySingleProject(projectPath) + } else { + const records: { valid: boolean; path: string }[] = [] + fs.readdirSync(projectPath).forEach((dir) => { + const dirPath = path.join(projectPath, dir) + if (fs.existsSync(path.join(dirPath, "package.json"))) { + records.push({ path: dirPath, valid: verifySingleProject(dirPath) }) + logger.printDivider("=") + } + }) + printTable(records) + success = records.every((record) => record.valid) + } + return success +} +export default verifyCmd diff --git a/apps/cli/src/constants.ts b/apps/cli/src/constants.ts new file mode 100644 index 0000000..9619a4e --- /dev/null +++ b/apps/cli/src/constants.ts @@ -0,0 +1,31 @@ +import path from "path" +import { fileURLToPath } from "url" + +const filepath = fileURLToPath(import.meta.url) +const filename = path.basename(filepath) +const __dirname = path.dirname(filepath) +const isInJs = filename.endsWith(".js") + +function inferNodeEnv() { + if (isInJs) { + return "production" + } + if (process.env.NODE_ENV) { + return process.env.NODE_ENV + } + return "development" +} + +export const NODE_ENV = inferNodeEnv() + +export function getRootDir() { + return isInJs ? __dirname : path.dirname(__dirname) +} + +export function getDockerFolder() { + return isInJs ? path.join(getRootDir(), "docker") : path.join(getRootDir(), "src/docker") +} + +export function getDockerEntrypoint() { + return path.join(getDockerFolder(), "entrypoint.sh") +} diff --git a/apps/cli/src/docker/entrypoint.sh b/apps/cli/src/docker/entrypoint.sh new file mode 100755 index 0000000..c469e86 --- /dev/null +++ b/apps/cli/src/docker/entrypoint.sh @@ -0,0 +1,17 @@ +. ~/.bashrc + +cd /workspace +rm *.tgz +rm -rf node_modules +cp -r /workspace /workspace-copy +cd /workspace-copy +pnpm i +pnpm run build +npm pack +# check number of *.tgz file in current directory +# if more than 1, then exit with error +if [ $(ls -1 *.tgz 2>/dev/null | wc -l) -gt 1 ]; then + echo "More than one tgz file found" + exit 1 +fi +cp *.tgz /workspace diff --git a/apps/cli/src/logger.ts b/apps/cli/src/logger.ts new file mode 100644 index 0000000..1046dee --- /dev/null +++ b/apps/cli/src/logger.ts @@ -0,0 +1,17 @@ +import chalk from "chalk" +import debug from "debug" + +debug.enable("*") + +export function printDivider(char: string = "=") { + const divider = chalk.blue(char.repeat(process.stdout.columns)) + console.log(divider) +} + +export default { + debug: debug("debug"), + info: debug("info"), + warn: debug("warn"), + error: debug("error"), + printDivider +} diff --git a/apps/cli/src/types.ts b/apps/cli/src/types.ts new file mode 100644 index 0000000..66c5a73 --- /dev/null +++ b/apps/cli/src/types.ts @@ -0,0 +1,9 @@ +import type { ExtPackageJson } from "@kksh/api/models" + +export type BuildResult = { + shasum: string + tarballFilename: string + tarballPath: string + extPath: string + pkg: ExtPackageJson +} diff --git a/apps/cli/src/utils.ts b/apps/cli/src/utils.ts new file mode 100644 index 0000000..863ca5b --- /dev/null +++ b/apps/cli/src/utils.ts @@ -0,0 +1,179 @@ +import { exec, spawn } from "child_process" +import crypto from "crypto" +import path from "path" +import { ExtPackageJson } from "@kksh/api/models" +import fs from "fs-extra" +import * as v from "valibot" +import { getDockerEntrypoint } from "./constants" +import type { BuildResult } from "./types" + +/** + * Package Name can be scoped or not + * Use regex to extract package name + * @param packageName + * @param version + */ +export function computeTarballName(packageName: string, version: string): string { + const scoped = packageName.startsWith("@") + if (scoped) { + const [scope, name] = packageName.split("/") + return `${scope.substring(1)}-${name}-${version}.tgz` + } else { + return `${packageName}-${version}.tgz` + } +} + +export function computeFileHash(filePath: string, algorithm: string): Promise { + return new Promise((resolve, reject) => { + const hash = crypto.createHash(algorithm) + const stream = fs.createReadStream(filePath) + + stream.on("data", (data) => { + // @ts-ignore + hash.update(data) + }) + + stream.on("end", () => { + const shasum = hash.digest("hex") + resolve(shasum) + }) + + stream.on("error", (err) => { + reject(err) + }) + }) +} + +export function computeFileSha1(filePath: string): Promise { + return computeFileHash(filePath, "sha1") +} + +export function computeFileSha512(filePath: string): Promise { + return computeFileHash(filePath, "sha512") +} + +export function computeHash(buffer: Buffer, algorithm: "sha1" | "sha256" | "sha512") { + const hash = crypto.createHash(algorithm) + // @ts-ignore + hash.update(buffer) + return hash.digest("hex") +} + +/** + * Docker is used to build each individual extension for safety + * Packages could potentially modify other extensions if they share environment. + * There is also a possibility of leaking environment variables. + * docker run -v $(pwd)/scripts/docker/entrypoint.sh:/entrypoint.sh \ + * -v $(pwd)/extensions/$ext:/workspace \ + * -w /workspace --rm \ + * --platform=linux/amd64 \ + * node:20 /entrypoint.sh + * @param extPath + * @returns shasum of the tarball parsed from stderr output + */ +export function buildWithDocker(extPath: string): Promise<{ + stderrShasum: string + stderrTarballFilename: string + pkg: ExtPackageJson +}> { + console.log(`Building ${extPath}`) + return new Promise((resolve, reject) => { + const pkg = v.parse(ExtPackageJson, fs.readJsonSync(path.join(extPath, "package.json"))) + const dockerEntrypoint = getDockerEntrypoint() + console.log("Docker Entrypoint", dockerEntrypoint) + + const dockerCmd = ` + run -v ${dockerEntrypoint}:/entrypoint.sh -v ${extPath}:/workspace -w /workspace --rm huakunshen/kunkun-ext-builder:latest /entrypoint.sh` + console.log("dockerCmd", dockerCmd) + const args = dockerCmd + .split(" ") + .filter((arg) => arg.length > 0) + .filter((arg) => arg !== "\n") + const subprocess = spawn("docker", args) + let stderrShasum = "" + let stderrTarballFilename = "" + subprocess.stdout.on("data", (data) => { + console.log(`stdout: ${data}`) + }) + subprocess.stderr.on("data", (data) => { + const dataStr = data.toString() + console.error(`stderr: ${dataStr}`) + // if (data instanceof String) { + if (dataStr.includes("npm notice shasum")) { + console.log("shasum found") + const shasumMatch = dataStr.match(/npm notice shasum:\s+([a-f0-9]+)/) + + if (shasumMatch) { + stderrShasum = shasumMatch[1] + console.log("Parsed shasum:", stderrShasum) + } + } + + if (dataStr.includes("npm notice filename:")) { + const tarballFilename = dataStr.match(/npm notice filename:\s+([^\s]+)/) + if (tarballFilename) { + stderrTarballFilename = tarballFilename[1] + console.log("Parsed tarball:", stderrTarballFilename) + } + } else if (dataStr.includes("filename:")) { + const tarballFilename = dataStr.match(/filename:\s+([^\s]+)/) + if (tarballFilename) { + stderrTarballFilename = tarballFilename[1] + console.log("Parsed tarball:", stderrTarballFilename) + } + } + // } else { + // console.error("data is not string"); + // } + }) + subprocess.on("close", (code) => { + console.log(`child process exited with code ${code}`) + if (stderrShasum.trim().length === 0 || stderrTarballFilename.trim().length === 0) { + return reject("shasum or tarball filename not found") + } + if (code !== 0) { + return reject(`child process exited with code ${code}`) + } else { + return resolve({ stderrShasum, stderrTarballFilename, pkg }) + } + }) + }) +} + +/** + * Use this function to build an extension with docker and validate the tarball + * If this passes, the tarball is ready to be inserted into the database + * @param extPath Extension Path + * @returns + */ +export function buildWithDockerAndValidate(extPath: string): Promise { + return buildWithDocker(extPath) + .then((res) => { + const parsedTarballPath = path.join(extPath, res.stderrTarballFilename) + if (!fs.existsSync(parsedTarballPath)) { + console.error(`Tarball not found: ${parsedTarballPath}`) + process.exit(1) + } + return computeFileSha1(parsedTarballPath).then((computedShasum) => { + if (computedShasum !== res.stderrShasum) { + console.error( + `Shasum mismatch: Computed(${computedShasum}) !== Output from docker(${res.stderrShasum})` + ) + process.exit(1) + } else { + console.log("Shasum matches") + } + return { + shasum: computedShasum, + tarballFilename: res.stderrTarballFilename, + tarballPath: parsedTarballPath, + extPath: extPath, + pkg: res.pkg + } + }) + }) + .catch((err) => { + console.error(err) + process.exit(1) + }) +} diff --git a/apps/cli/tsconfig.json b/apps/cli/tsconfig.json new file mode 100644 index 0000000..359dd73 --- /dev/null +++ b/apps/cli/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false, + "baseUrl": ".", + "paths": { + "@/*": ["src/*"], + "~/*": ["src/*"] + }, + "outDir": "dist", + "declaration": true, + "declarationMap": true + } +} diff --git a/apps/create-kunkun/.gitignore b/apps/create-kunkun/.gitignore new file mode 100644 index 0000000..9b1ee42 --- /dev/null +++ b/apps/create-kunkun/.gitignore @@ -0,0 +1,175 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/apps/create-kunkun/CHANGELOG.md b/apps/create-kunkun/CHANGELOG.md new file mode 100644 index 0000000..89ccbf5 --- /dev/null +++ b/apps/create-kunkun/CHANGELOG.md @@ -0,0 +1,116 @@ +# create-kunkun + +## 0.1.30 + +### Patch Changes + +- Updated dependencies + - @kksh/api@0.0.23 + +## 0.1.29 + +### Patch Changes + +- Updated dependencies + - @kksh/api@0.0.22 + +## 0.1.28 + +### Patch Changes + +- Re-enable server-based dev extension refresh. Deep Link forces switch focus to kunkun +- Updated dependencies + - @kksh/api@0.0.21 + +## 0.1.27 + +### Patch Changes + +- Fix Some Windows incompatibilities +- Updated dependencies + - @kksh/api@0.0.20 + +## 0.1.26 + +### Patch Changes + +- Fix windows compatibility errors + +## 0.1.23 + +### Patch Changes + +- Store templates as .tgz in dist, to avoid missing .gitignore problem + +## 0.1.18 + +### Patch Changes + +- Update templates README with detailed instructions + +## 0.1.17 + +### Patch Changes + +- Updated dependencies [9fa3d8e] +- Updated dependencies [0a1ab7c] +- Updated dependencies [b48d53b] + - @kksh/api@0.0.20 + +## 0.1.16 + +### Patch Changes + +- Updated dependencies [9fa3d8e] +- Updated dependencies [0a1ab7c] +- Updated dependencies [b48d53b] + - @kksh/api@0.0.20 + +## 0.1.13 + +### Patch Changes + +- Updated dependencies + - @kksh/api@0.0.11 + +## 0.1.12 + +### Patch Changes + +- Updated dependencies + - @kksh/api@0.0.10 + +## 0.1.11 + +### Patch Changes + +- Update templates, custom ui templates now requires base url. Due to a Tauri API problem on Windows. + +## 0.1.10 + +### Patch Changes + +- Updated dependencies + - @kksh/api@0.0.9 + +## 0.1.7 + +### Patch Changes + +- Add some helper functions for template worker extension building. Replace rollup with bun. +- Updated dependencies + - @kksh/api@0.0.6 + +## 0.1.6 + +### Patch Changes + +- Updated dependencies [ec47e1e] + - @kksh/api@0.0.5 + +## 0.1.5 + +### Patch Changes + +- Updated dependencies + - @kksh/api@0.0.4 diff --git a/apps/create-kunkun/README.md b/apps/create-kunkun/README.md new file mode 100644 index 0000000..01e5733 --- /dev/null +++ b/apps/create-kunkun/README.md @@ -0,0 +1,16 @@ +# Kunkun Extension Initializer + +## Usage + +```bash +npm init kunkun@latest + +npx create-kunkun@latest +``` + +## Develop + +```bash +bun index.ts --help +bun index.ts +``` diff --git a/apps/create-kunkun/__tests__/create-template.test.ts b/apps/create-kunkun/__tests__/create-template.test.ts new file mode 100644 index 0000000..b6c4e4f --- /dev/null +++ b/apps/create-kunkun/__tests__/create-template.test.ts @@ -0,0 +1,42 @@ +/** + * This is a E2E test, create every template from production build and run `npm install` and `npm run build` + * When running `npm install` with bun shell, it fails in bun test environment, so I simply run everything as regular ts without test() + */ +import { $ } from "bun" +import { afterAll, beforeAll, describe, expect, test } from "bun:test" +import os from "os" +import path from "path" +import fs from "fs-extra" +import { getRootDir } from "../src/constants" + +const testDir = path.join(os.tmpdir(), "kunkun-create-kunkun-test") +console.log("Test Dir: ", testDir) +const distDir = path.join(getRootDir(), "dist") +const indexjsPath = path.join(distDir, "index.mjs") +const templateNames = ["template", "react", "vue", "nuxt", "svelte", "sveltekit"] + +fs.rmdirSync(testDir, { recursive: true }) +fs.mkdirpSync(testDir) +await Promise.all( + templateNames.map(async (templateName) => { + const folderName = `${templateName}-ext` + await $`node ${indexjsPath} --outdir ${testDir} --name ${folderName} --template ${templateName}` + const templateDir = path.join(testDir, folderName) + await $`rm -rf node_modules`.cwd(templateDir).text() // this doesn't work within bun test + await $`pnpm install`.cwd(templateDir).text() // this doesn't work within bun test + await $`pnpm run build`.cwd(templateDir).text() + }) +) + +test("Build Artifact Existence", () => { + templateNames.forEach(async (templateName) => { + const expectedOutDir = templateName === "sveltekit" ? "build" : "dist" + const folderName = `${templateName}-ext` + const templateDir = path.join(testDir, folderName) + expect(fs.existsSync(path.join(templateDir, expectedOutDir))).toBeTrue() + }) +}) + +afterAll(() => { + fs.rmdirSync(testDir, { recursive: true }) +}) diff --git a/apps/create-kunkun/build.ts b/apps/create-kunkun/build.ts new file mode 100644 index 0000000..0dabea8 --- /dev/null +++ b/apps/create-kunkun/build.ts @@ -0,0 +1,79 @@ +import os from "os" +import path from "path" +import { $ } from "bun" +import chalk from "chalk" +import fs from "fs-extra" +import getFolderSize from "get-folder-size" +import { getRootDir } from "./src/constants" +import { cleanExtension, patchManifestJsonSchema, patchPkgJsonDep } from "./src/patch" +import { tarCompress } from "./src/utils" + +await $`rm -rf dist` +await $`bun build index.ts --outfile=dist/index.mjs --target node`.env({ + NODE_ENV: "production" +}) +// await $`pnpm rolldown -c` + +/* -------------------------------------------------------------------------- */ +/* Post Build */ +/* -------------------------------------------------------------------------- */ + +const distPath = path.join(getRootDir(), "dist") +const distTemplatesPath = path.join(distPath, "templates") +const tmpDistTemplatesPath = path.join(distPath, "tmp-templates") +// clear distTemplatesPath +fs.emptyDirSync(distTemplatesPath) +fs.emptyDirSync(tmpDistTemplatesPath) + +/* -------------------------------------------------------------------------- */ +/* copy ../../templates to dist/templates */ +/* -------------------------------------------------------------------------- */ +console.log(getRootDir()) + +const templatesPath = path.join(getRootDir(), "../..", "templates") +fs.copySync(templatesPath, tmpDistTemplatesPath, { dereference: os.platform() === "win32" }) + +/* -------------------------------------------------------------------------- */ +/* Clean Dist Folder */ +/* -------------------------------------------------------------------------- */ +for (const p of fs.readdirSync(tmpDistTemplatesPath)) { + console.log("Clean Extension", path.join(tmpDistTemplatesPath, p)) + cleanExtension(path.join(tmpDistTemplatesPath, p)) +} + +/* -------------------------------------------------------------------------- */ +/* Patch Templates */ +/* -------------------------------------------------------------------------- */ +for (const p of fs.readdirSync(tmpDistTemplatesPath)) { + const pkgJsonPath = path.join(tmpDistTemplatesPath, p, "package.json") + if (fs.existsSync(pkgJsonPath)) { + /* ----------------------- Patch Package Dependencies ----------------------- */ + // Replace local dependencies (workspace:*) with real dependencies + await patchPkgJsonDep(pkgJsonPath) + /* ----------------------- Patch Manifest JSON Schema ----------------------- */ + // Replace local template with remote schema + patchManifestJsonSchema(pkgJsonPath) + // remove node_modules + fs.rmdirSync(path.join(distPath, "templates", p, "node_modules"), { recursive: true }) + } +} + +/* -------------------------------------------------------------------------- */ +/* Zip Templates */ +/* -------------------------------------------------------------------------- */ +for (const p of fs.readdirSync(tmpDistTemplatesPath)) { + const src = path.join(tmpDistTemplatesPath, p) + // skip if src is not a directory + if (!fs.statSync(src).isDirectory()) { + continue + } + const dest = path.join(distTemplatesPath, `${p}.tgz`) + console.log(`${chalk.green("Zipping")} ${chalk.blue(src)} to ${chalk.blue(dest)}`) + await tarCompress(src, dest) +} + +fs.rmSync(tmpDistTemplatesPath, { recursive: true }) + +// get total folder size of distTemplatesPath +const size = await getFolderSize.loose(distTemplatesPath) +console.log(`dist size ${(size / 1000 / 1000).toFixed(2)} MB`) diff --git a/apps/create-kunkun/bun.lockb b/apps/create-kunkun/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..65e2eed1b8def6b26a8cba0475ef27896842a7d5 GIT binary patch literal 3143 zcmd5;YfKbZ6rP0zaZyl2h^E@b2S#CMAFzPzx>T$U?e-yPFj{H5EVIkNunRK-3uxNK z_@Ipzl|DB7@X=MGVrwy0D26_uQ037ON=%2rxqI%N zIo~(uo^xl;Ss0-%`=|&d9wrCo}xgPqcNNzx#bpT_6~NOc_x`z~8i6T<2l~VLgi=-1A_j z0KEzHRM5vj1DSge^fb^1#p_@z*lA)6u1hIQb({avMzf~u_U3E$&hn6-7wlDU+#UJB zise(C#@2zixK%m8tqf#QyEXpT708s`CNw3Z&U2g3~q4vir1^xhq!*7Q*(xS_vY;`-K)jXq`smwH_dm+qRp53XZamUF`wT0 zQ$1}Rsv|xPH+DXxoq#Z<#*N2F?6WP6)AQ-z0)^)+TRK(ZBAFYkJvgXZaI~S=Nw|O>{_-#^`Z#lIu z;ppy~`-?=p9KAvFj$u zfxoZ#+l#r7E*CjWP{@%ejL+-hG71x5S8=00Y5%$_T-R}(o*{cdPijaFLEz2+SNZ@S z#==t^PY}3Qz_s5;rX}GQPY?&;si=#<)!#?SCT^9TsLS&(aRNNvRzRLc(Yec+M&? zesVAL2>ZsKP}Gp2Yrz-2#=&_74LrwDEU;yKsVqygneR9uiKs8ymZ0yD6$cI95j{95 z7GgL95O3}M!cFj0UcQClDHT=Bt6WYtmty&ne2TNObUy!>FjYe%n9cL~T%uadQg)i- z*%H!`pO?dtPS&m-Gg7ONsR{)@j^yp7Xdy6+gC4aJ!GgU&p^ZlzBD;$dCk|&8H4dG` z3J8(0Y9y`NSj1N7$fB+EgicA#fJVAh31>l%$R>8f#R1yIsf0796+$AUb0mf1;Kxz` T1Y-+$xSotbJi;0IAO8IbJOW9M literal 0 HcmV?d00001 diff --git a/apps/create-kunkun/index.ts b/apps/create-kunkun/index.ts new file mode 100644 index 0000000..6f6e1cd --- /dev/null +++ b/apps/create-kunkun/index.ts @@ -0,0 +1,193 @@ +#!/usr/bin/env node +import path from "path" +import { input, select } from "@inquirer/prompts" +import { version as kkApiVersion } from "@kksh/api/package.json" +import chalk from "chalk" +import { Command, Option } from "commander" +import fs from "fs-extra" +import pkgJson from "./package.json" +import { createKunkunVersion, getTemplateRoot, isProduction } from "./src/constants" +import { cleanExtension, patchHBS, patchManifestJsonSchema, patchPkgJsonDep } from "./src/patch" +import { getLatestNpmPkgVersion, tarExtract } from "./src/utils" + +console.log(`${chalk.blue("create-kunkun version:")} ${createKunkunVersion}`) +const latestCreateKunkunVersion = await getLatestNpmPkgVersion("create-kunkun") +console.log(`${chalk.blue("Latest create-kunkun version:")} latestCreateKunkunVersion`) +if (latestCreateKunkunVersion !== createKunkunVersion) { + const msg = `You are using create-kunkun version ${createKunkunVersion}, but the latest version is ${latestCreateKunkunVersion}. It may not work with the latest Kunkun app.` + console.warn(chalk.red(msg)) +} + +const cwd = process.cwd() +const templateRoot = getTemplateRoot() +console.info(`${chalk.blue("Current Working Directory")}: ${cwd}`) +console.info(`${chalk.blue("Template Root:")}`, templateRoot) +if (!fs.existsSync(templateRoot)) { + console.error(`Template directory not found; Expected at ${templateRoot}`) + process.exit(1) +} +const program = new Command() + +program + .version(pkgJson.version) + .addOption( + new Option("-t, --template