diff --git a/CHANGELOG.md b/CHANGELOG.md index f1b36172..40fd479c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,12 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## Unreleased +## [0.21.2](https://github.com/o1-labs/zkapp-cli/compare/0.21.0...0.21.2) - 2024-05-20 + +### Changed + +- Migrate **Next.js** UI scaffold to **Next.js** `v14.2.3` and fix the latest `o1js` usage issue. [#652](https://github.com/o1-labs/zkapp-cli/pull/652) + ## [0.21.0](https://github.com/o1-labs/zkapp-cli/compare/0.20.1...0.21.0) - 2024-05-16 ### Breaking changes @@ -26,7 +32,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Changed - Update ZkProgram proof detection during deployment [#649](https://github.com/o1-labs/zkapp-cli/pull/649) - - Improved `SmartContract` classes inheritance lookup used during the zkApps deployment procedure. [#640](https://github.com/o1-labs/zkapp-cli/pull/640) ### Fixed diff --git a/package-lock.json b/package-lock.json index f8c02ef4..8b81cda2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "zkapp-cli", - "version": "0.21.1", + "version": "0.21.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "zkapp-cli", - "version": "0.21.1", + "version": "0.21.2", "license": "Apache-2.0", "dependencies": { "acorn": "^8.11.3", diff --git a/package.json b/package.json index 50acf9c2..ad29957a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zkapp-cli", - "version": "0.21.1", + "version": "0.21.2", "description": "CLI to create zkApps (zero-knowledge apps) for Mina Protocol", "homepage": "https://github.com/o1-labs/zkapp-cli/", "keywords": [ diff --git a/src/lib/project.js b/src/lib/project.js index 1bcf824c..a7489c9b 100644 --- a/src/lib/project.js +++ b/src/lib/project.js @@ -100,7 +100,7 @@ export async function project({ name, ui }) { // Add dependencies object if none is found in the package.json because generated // SvelteKit projects do not have dependencies included. if (!pkgJson.dependencies) pkgJson['dependencies'] = {}; - pkgJson.dependencies.o1js = '0.*'; + pkgJson.dependencies.o1js = '^1.*'; fs.writeJSONSync(path.join('ui', 'package.json'), pkgJson, { spaces: 2 }); // Use `install`, not `ci`, b/c these won't have package-lock.json yet. @@ -268,7 +268,7 @@ function scaffoldSvelte() { path.join('ui', 'src') ); - const customTsConfig = ` { + const customTsConfig = `{ "extends": "./.svelte-kit/tsconfig.json", "compilerOptions": { "target": "es2020", @@ -394,7 +394,7 @@ async function scaffoldNext(projectName) { // set the project name and default flags // https://nextjs.org/docs/api-reference/create-next-app#options let args = [ - 'create-next-app@13.4.1', + 'create-next-app@14.2.3', 'ui', '--use-npm', '--src-dir', @@ -421,17 +421,15 @@ async function scaffoldNext(projectName) { useTypescript = false; } - const nextConfig = fs.readFileSync(path.join('ui', 'next.config.js'), 'utf8'); + const nextConfig = fs.readFileSync( + path.join('ui', 'next.config.mjs'), + 'utf8' + ); let newNextConfig = nextConfig.replace( - /^}(.*?)$/gm, // Search for the last '}' in the file. + /^};(.*?)$/gm, // Search for the last '};' in the file. ` - webpack(config) { - config.resolve.alias = { - ...config.resolve.alias, - o1js: require('path').resolve('node_modules/o1js') - }; config.experiments = { ...config.experiments, topLevelAwait: true }; return config; }, @@ -463,7 +461,7 @@ async function scaffoldNext(projectName) { 'reactStrictMode: false' ); - fs.writeFileSync(path.join('ui', 'next.config.js'), newNextConfig); + fs.writeFileSync(path.join('ui', 'next.config.mjs'), newNextConfig); const indexFileName = useTypescript ? 'index.tsx' : 'index.js'; @@ -494,38 +492,37 @@ async function scaffoldNext(projectName) { path.join('ui', 'public', 'assets') ); - const tsconfig = ` - { - "compilerOptions": { - "target": "es2020", - "module": "esnext", - "lib": ["dom", "dom.iterable","esnext"], - "strict": true, - "strictPropertyInitialization": false, // to enable generic constructors, e.g. on CircuitValue - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "esModuleInterop": true, - "moduleResolution": "node", - "experimentalDecorators": true, - "emitDecoratorMetadata": true, - "allowJs": true, - "declaration": true, - "sourceMap": true, - "noFallthroughCasesInSwitch": true, - "allowSyntheticDefaultImports": true, - "isolatedModules": true, - "noEmit": true, - "incremental": true, - "resolveJsonModule": true, - "jsx": "preserve", - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"] + const tsconfig = `{ + "compilerOptions": { + "target": "es2020", + "module": "esnext", + "lib": ["dom", "dom.iterable", "esnext"], + "strict": true, + "strictPropertyInitialization": false, // to enable generic constructors, e.g. on CircuitValue + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "esModuleInterop": true, + "moduleResolution": "node", + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "allowJs": true, + "declaration": true, + "sourceMap": true, + "noFallthroughCasesInSwitch": true, + "allowSyntheticDefaultImports": true, + "isolatedModules": true, + "noEmit": true, + "incremental": true, + "resolveJsonModule": true, + "jsx": "preserve", + "paths": { + "@/*": ["./src/*"] } - `; + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} +`; if (useTypescript) { fs.writeFileSync(path.join('ui', 'tsconfig.json'), tsconfig); @@ -533,19 +530,22 @@ async function scaffoldNext(projectName) { // Add a script to the package.json let x = fs.readJsonSync(path.join('ui', 'package.json')); x.scripts['ts-watch'] = 'tsc --noEmit --incremental --watch'; + x.scripts['build'] = 'next build --no-lint'; + x.type = 'module'; fs.writeJSONSync(path.join('ui', 'package.json'), x, { spaces: 2 }); } if (useGHPages) { const nextConfig = fs.readFileSync( - path.join('ui', 'next.config.js'), + path.join('ui', 'next.config.mjs'), 'utf8' ); console.log( - 'Using project name ' + - projectName + - ' for GitHub repo name. Change in next.config.js and pages/reactCOIServiceWorker.tsx if this is not correct or changes' + `Using project name '${projectName}' as the GitHub repository name.` + ); + console.log( + "Please update it in 'next.config.mjs' and 'pages/reactCOIServiceWorker.tsx' files if this is not correct or if it will be changed.\n" ); let newNextConfig = nextConfig.replace( @@ -555,15 +555,15 @@ async function scaffoldNext(projectName) { unoptimized: true, }, output: 'export', - /* Used to serve the Next.js app from a subdirectory (the GitHub repo name) and - * assetPrefix is used to serve assets (JS, CSS, images, etc.) from that subdirectory + /* Used to serve the Next.js app from a subdirectory (the GitHub repo name) and + * assetPrefix is used to serve assets (JS, CSS, images, etc.) from that subdirectory * when deployed to GitHub Pages. The assetPrefix needs to be added manually to any assets - * if they're not loaded by Next.js' automatic handling (for example, in CSS files or in a element). - * The 'ghp-postbuild.js' script in this project prepends the repo name to asset urls in the built css files + * if they're not loaded by Next.js' automatic handling (for example, in CSS files or in a element). + * The 'ghp-postbuild.js' script in this project prepends the repo name to asset urls in the built css files * after runing 'npm run deploy'. */ - basePath: process.env.NODE_ENV === 'production' ? '/${projectName}' : '', // update if your repo name changes for 'npm run deploy' to work successfully - assetPrefix: process.env.NODE_ENV === 'production' ? '/${projectName}/' : '', // update if your repo name changes for 'npm run deploy' to work successfully + basePath: process.env.NODE_ENV === 'production' ? '/${projectName}' : '', // update if your repo name changes for 'npm run deploy' to work correctly + assetPrefix: process.env.NODE_ENV === 'production' ? '/${projectName}/' : '', // update if your repo name changes for 'npm run deploy' to work correctly };` ); @@ -580,12 +580,11 @@ async function scaffoldNext(projectName) { pageExtensions: ['page.tsx', 'page.ts', 'page.jsx', 'page.js'],` ); - fs.writeFileSync(path.join('ui', 'next.config.js'), newNextConfig); + fs.writeFileSync(path.join('ui', 'next.config.mjs'), newNextConfig); // Add some scripts to the package.json let x = fs.readJsonSync(`ui/package.json`); - x.scripts['export'] = 'next export'; - const deployScript = `next build && next export && ${ + const deployScript = `next build --no-lint && ${ isWindows ? `type nul > ${path.join('out', '.nojekyll')}` : `touch ${path.join('out', '.nojekyll')}` @@ -647,13 +646,15 @@ export default function` fs.writeFileSync( path.join('ui', 'src', 'pages', reactCOIServiceWorkerFileName), - ` -export {} + `export {}; function loadCOIServiceWorker() { - if (typeof window !== 'undefined' && window.location.hostname != 'localhost') { + if ( + typeof window !== 'undefined' && + window.location.hostname != 'localhost' + ) { const coi = window.document.createElement('script'); - coi.setAttribute('src','/${projectName}/coi-serviceworker.min.js'); // update if your repo name changes for npm run deploy to work successfully + coi.setAttribute('src', '/${projectName}/coi-serviceworker.min.js'); // update if your repo name changes for 'npm run deploy' to work correctly window.document.head.appendChild(coi); } } @@ -697,14 +698,11 @@ function scaffoldNuxt() { const nuxtConfig = fs.readFileSync(path.join('ui', 'nuxt.config.ts'), 'utf8'); let newNuxtConfig = nuxtConfig.replace( 'export default defineNuxtConfig({', - ` - export default defineNuxtConfig({ - + `export default defineNuxtConfig({ vite: { build: { target: "esnext" }, optimizeDeps: { esbuildOptions: { target: "esnext" } }, }, - css: ['~/assets/styles/globals.css'], ` ); diff --git a/src/lib/ui/next/ghp-postbuild.js b/src/lib/ui/next/ghp-postbuild.js index da33db93..1d58db28 100644 --- a/src/lib/ui/next/ghp-postbuild.js +++ b/src/lib/ui/next/ghp-postbuild.js @@ -1,26 +1,25 @@ -const fs = require('fs'); -const path = require('path'); +import { readdirSync, readFileSync, writeFileSync } from 'fs'; +import { dirname, extname, join } from 'path'; +import { fileURLToPath } from 'url'; -// This script modifies the built CSS files and prepends the repo-name to the asset URLs. -// to be compatible with github pages deployment. -const cssDir = path.join(__dirname, '/out/_next/static/css'); +// This script modifies the built CSS files and prepends the repo-name to the asset URLs +// to be compatible with the GitHub pages deployments. +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +const cssDir = join(__dirname, '/out/_next/static/css'); // Update your repository name here if it is different from the project name. let repoURL = ''; -const files = fs.readdirSync(cssDir); +const files = readdirSync(cssDir); files.forEach((file) => { - if (path.extname(file) === '.css') { - const filePath = path.join(cssDir, file); - - const data = fs.readFileSync(filePath, 'utf8'); - + if (extname(file) === '.css') { + const filePath = join(cssDir, file); + const data = readFileSync(filePath, 'utf8'); const singleQuoteRegex = new RegExp(`url\\(\\s*'\\/(?!${repoURL})`, 'g'); const doubleQuoteRegex = new RegExp(`url\\(\\s*"\\/(?!${repoURL})`, 'g'); - let result = data.replace(singleQuoteRegex, `url('/${repoURL}/`); result = result.replace(doubleQuoteRegex, `url("/${repoURL}/`); - - fs.writeFileSync(filePath, result, 'utf8'); + writeFileSync(filePath, result, 'utf8'); } }); diff --git a/templates/project-ts/package.json b/templates/project-ts/package.json index 8f337ea0..3f729270 100644 --- a/templates/project-ts/package.json +++ b/templates/project-ts/package.json @@ -36,7 +36,7 @@ "typescript": "^5.1" }, "peerDependencies": { - "o1js": "1.*" + "o1js": "^1.*" }, "engines": { "node": ">=18.14.0"