From 400b27d1778ff2a720036e3f6d130f97a504d5ac Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 3 Apr 2025 16:22:06 +0200 Subject: [PATCH 1/7] feat: copy package json for sputnik build --- src/constants/dev.constants.ts | 4 +++ src/services/build/build.javascript.ts | 39 ++++++++++++++++++++++++-- src/types/pkg.ts | 13 +++++++++ src/utils/pkg.utils.ts | 9 ++---- 4 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 src/types/pkg.ts diff --git a/src/constants/dev.constants.ts b/src/constants/dev.constants.ts index d208b4fe..034f442a 100644 --- a/src/constants/dev.constants.ts +++ b/src/constants/dev.constants.ts @@ -38,6 +38,10 @@ export const IC_WASM_MIN_VERSION = '0.8.5'; export const DOCKER_MIN_VERSION = '24.0.0'; export const DEPLOY_LOCAL_REPLICA_PATH = join(process.cwd(), 'target', 'deploy'); +export const PACKAGE_JSON_PATH = join(process.cwd(), 'package.json'); export const SPUTNIK_INDEX_MJS = 'sputnik.index.mjs'; export const DEPLOY_SPUTNIK_PATH = join(DEPLOY_LOCAL_REPLICA_PATH, SPUTNIK_INDEX_MJS); + +export const SPUTNIK_PACKAGE_JSON = 'sputnik.package.json'; +export const PACKAGE_JSON_SPUTNIK_PATH = join(DEPLOY_LOCAL_REPLICA_PATH, SPUTNIK_PACKAGE_JSON); diff --git a/src/services/build/build.javascript.ts b/src/services/build/build.javascript.ts index a510f644..986074e6 100644 --- a/src/services/build/build.javascript.ts +++ b/src/services/build/build.javascript.ts @@ -1,16 +1,22 @@ +import {notEmptyString} from '@dfinity/utils'; +import {isEmptyString} from '@dfinity/utils/dist/types/utils/nullish.utils'; import {buildEsm, execute} from '@junobuild/cli-tools'; import {green, magenta, red, yellow} from 'kleur'; -import {mkdir} from 'node:fs/promises'; +import {existsSync} from 'node:fs'; +import {mkdir, writeFile} from 'node:fs/promises'; import {join} from 'node:path'; import { DEPLOY_LOCAL_REPLICA_PATH, DEPLOY_SPUTNIK_PATH, DEVELOPER_PROJECT_SATELLITE_PATH, INDEX_MJS, - INDEX_TS + INDEX_TS, + PACKAGE_JSON_PATH, + PACKAGE_JSON_SPUTNIK_PATH } from '../../constants/dev.constants'; import type {BuildArgs, BuildLang} from '../../types/build'; import {formatBytes, formatTime} from '../../utils/format.utils'; +import {readPackageJson} from '../../utils/pkg.utils'; import {detectPackageManager} from '../../utils/pm.utils'; import {confirmAndExit} from '../../utils/prompt.utils'; @@ -98,3 +104,32 @@ const hasEsbuild = async (): Promise => { return false; } }; + +const copyMetadata = async (): Promise => { + if (!existsSync(PACKAGE_JSON_PATH)) { + // No package.json therefore no metadata to pass to the build in the container. + return; + } + + try { + const {juno, version} = await readPackageJson(); + + if (isEmptyString(juno?.functions?.version) && isEmptyString(version)) { + // No version detected therefore no metadata to the build in the container. + return; + } + + const functionsVersion = juno?.functions?.version; + + const packageJson = { + ...(notEmptyString(version) && {version}), + ...(notEmptyString(functionsVersion) && {juno}) + }; + + await writeFile(PACKAGE_JSON_SPUTNIK_PATH, JSON.stringify(packageJson, null, 2), 'utf-8'); + } catch (err: unknown) { + // We want to continue the build process even if copying package.json fails, + // since it's only used to set the extended custom version. + console.log('⚠️ Could not copy package.json for the build.'); + } +}; diff --git a/src/types/pkg.ts b/src/types/pkg.ts new file mode 100644 index 00000000..23209e4f --- /dev/null +++ b/src/types/pkg.ts @@ -0,0 +1,13 @@ +export interface PackageJson { + dependencies?: Record; + version?: string; + juno?: PackageJsonJuno; +} + +export interface PackageJsonJuno { + functions?: PackageJsonJunoFunctions; +} + +export interface PackageJsonJunoFunctions { + version?: string; +} diff --git a/src/utils/pkg.utils.ts b/src/utils/pkg.utils.ts index 0bb8686a..cdebc58b 100644 --- a/src/utils/pkg.utils.ts +++ b/src/utils/pkg.utils.ts @@ -1,12 +1,9 @@ import {readFile} from 'node:fs/promises'; -import {join} from 'node:path'; - -export interface PackageJson { - dependencies?: Record; -} +import {PACKAGE_JSON_PATH} from '../constants/dev.constants'; +import { PackageJson } from "../types/pkg"; export const readPackageJson = async (): Promise => { - const packageJson = await readFile(join(process.cwd(), 'package.json'), 'utf-8'); + const packageJson = await readFile(PACKAGE_JSON_PATH, 'utf-8'); // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const {dependencies} = JSON.parse(packageJson) as {dependencies?: Record}; From 2409d9fcc1f63608656ace6645cbf4e4361cd4af Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 3 Apr 2025 18:10:12 +0200 Subject: [PATCH 2/7] chore: lint --- src/utils/pkg.utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/pkg.utils.ts b/src/utils/pkg.utils.ts index cdebc58b..8e3edd23 100644 --- a/src/utils/pkg.utils.ts +++ b/src/utils/pkg.utils.ts @@ -1,6 +1,6 @@ import {readFile} from 'node:fs/promises'; import {PACKAGE_JSON_PATH} from '../constants/dev.constants'; -import { PackageJson } from "../types/pkg"; +import type {PackageJson} from '../types/pkg'; export const readPackageJson = async (): Promise => { const packageJson = await readFile(PACKAGE_JSON_PATH, 'utf-8'); From 2b1c7e3792315155f684dca769d98282e97273d3 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 3 Apr 2025 18:11:13 +0200 Subject: [PATCH 3/7] feat: copy metadata --- src/services/build/build.javascript.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/services/build/build.javascript.ts b/src/services/build/build.javascript.ts index 986074e6..74c6b63d 100644 --- a/src/services/build/build.javascript.ts +++ b/src/services/build/build.javascript.ts @@ -35,6 +35,8 @@ const build = async (params: BuildArgsTsJs) => { await createTargetDir(); + await copyMetadata(); + await buildWithEsbuild(params); }; From a72b936f10ac9c5c3019d6090f0c5e316f840bf5 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 3 Apr 2025 18:11:25 +0200 Subject: [PATCH 4/7] chore: lint --- src/services/build/build.javascript.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/build/build.javascript.ts b/src/services/build/build.javascript.ts index 74c6b63d..010e6df3 100644 --- a/src/services/build/build.javascript.ts +++ b/src/services/build/build.javascript.ts @@ -129,7 +129,7 @@ const copyMetadata = async (): Promise => { }; await writeFile(PACKAGE_JSON_SPUTNIK_PATH, JSON.stringify(packageJson, null, 2), 'utf-8'); - } catch (err: unknown) { + } catch (_err: unknown) { // We want to continue the build process even if copying package.json fails, // since it's only used to set the extended custom version. console.log('⚠️ Could not copy package.json for the build.'); From 1a09b389e813c102acf0d167f0f387aec6816414 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 3 Apr 2025 18:13:29 +0200 Subject: [PATCH 5/7] fix: mumbo jumbo import --- src/services/build/build.javascript.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/services/build/build.javascript.ts b/src/services/build/build.javascript.ts index 010e6df3..ba25ee94 100644 --- a/src/services/build/build.javascript.ts +++ b/src/services/build/build.javascript.ts @@ -1,5 +1,4 @@ -import {notEmptyString} from '@dfinity/utils'; -import {isEmptyString} from '@dfinity/utils/dist/types/utils/nullish.utils'; +import {isEmptyString, notEmptyString} from '@dfinity/utils'; import {buildEsm, execute} from '@junobuild/cli-tools'; import {green, magenta, red, yellow} from 'kleur'; import {existsSync} from 'node:fs'; From 4b0e635b38ab6970dc44bdd6ab7c9999e80c872d Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Fri, 4 Apr 2025 07:15:39 +0200 Subject: [PATCH 6/7] feat: copy metadata and print version --- src/services/build/build.javascript.ts | 73 +++++++++++++++++++++----- src/utils/pkg.utils.ts | 6 ++- 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/src/services/build/build.javascript.ts b/src/services/build/build.javascript.ts index ba25ee94..1c5e9f0c 100644 --- a/src/services/build/build.javascript.ts +++ b/src/services/build/build.javascript.ts @@ -1,5 +1,6 @@ -import {isEmptyString, notEmptyString} from '@dfinity/utils'; +import {isEmptyString, isNullish, notEmptyString} from '@dfinity/utils'; import {buildEsm, execute} from '@junobuild/cli-tools'; +import type {Metafile} from 'esbuild'; import {green, magenta, red, yellow} from 'kleur'; import {existsSync} from 'node:fs'; import {mkdir, writeFile} from 'node:fs/promises'; @@ -14,6 +15,7 @@ import { PACKAGE_JSON_SPUTNIK_PATH } from '../../constants/dev.constants'; import type {BuildArgs, BuildLang} from '../../types/build'; +import type {PackageJson} from '../../types/pkg'; import {formatBytes, formatTime} from '../../utils/format.utils'; import {readPackageJson} from '../../utils/pkg.utils'; import {detectPackageManager} from '../../utils/pm.utils'; @@ -34,12 +36,21 @@ const build = async (params: BuildArgsTsJs) => { await createTargetDir(); - await copyMetadata(); + const metadata = await prepareMetadata(); - await buildWithEsbuild(params); + await copyMetadata(metadata); + + const buildResult = await buildWithEsbuild(params); + + printResults({metadata, buildResult}); }; -const buildWithEsbuild = async ({lang, path}: BuildArgsTsJs) => { +interface BuildResult { + version: string; + output: [string, Metafile['outputs'][0]]; +} + +const buildWithEsbuild = async ({lang, path}: BuildArgsTsJs): Promise => { const infile = path ?? join(DEVELOPER_PROJECT_SATELLITE_PATH, lang === 'mjs' ? INDEX_MJS : INDEX_TS); @@ -67,10 +78,10 @@ const buildWithEsbuild = async ({lang, path}: BuildArgsTsJs) => { process.exit(1); } - const [key, {bytes}] = entry[0]; - - console.log(`${green('✔')} Build complete at ${formatTime()} (esbuild ${version})`); - console.log(`→ ${yellow(key)} (${formatBytes(bytes)})`); + return { + output: entry[0], + version + }; }; const createTargetDir = async () => { @@ -106,10 +117,12 @@ const hasEsbuild = async (): Promise => { } }; -const copyMetadata = async (): Promise => { +type BuildMetadata = Omit | undefined; + +const prepareMetadata = async (): Promise => { if (!existsSync(PACKAGE_JSON_PATH)) { // No package.json therefore no metadata to pass to the build in the container. - return; + return undefined; } try { @@ -117,20 +130,54 @@ const copyMetadata = async (): Promise => { if (isEmptyString(juno?.functions?.version) && isEmptyString(version)) { // No version detected therefore no metadata to the build in the container. - return; + return undefined; } const functionsVersion = juno?.functions?.version; - const packageJson = { + return { ...(notEmptyString(version) && {version}), ...(notEmptyString(functionsVersion) && {juno}) }; + } catch (_err: unknown) { + // We want to continue the build process even if copying package.json fails, + // since it's only used to set the extended custom version. + console.log('⚠️ Could not read build metadata from package.json.'); + return undefined; + } +}; + +const copyMetadata = async (metadata: BuildMetadata): Promise => { + if (isNullish(metadata)) { + // No metadata to pass to the build in the container. + return; + } - await writeFile(PACKAGE_JSON_SPUTNIK_PATH, JSON.stringify(packageJson, null, 2), 'utf-8'); + try { + await writeFile(PACKAGE_JSON_SPUTNIK_PATH, JSON.stringify(metadata, null, 2), 'utf-8'); } catch (_err: unknown) { // We want to continue the build process even if copying package.json fails, // since it's only used to set the extended custom version. console.log('⚠️ Could not copy package.json for the build.'); } }; + +const printResults = ({ + metadata, + buildResult +}: { + metadata: BuildMetadata; + buildResult: BuildResult; +}) => { + const {output, version: esbuildVersion} = buildResult; + const [key, {bytes}] = output; + + // The version defined by the developer for their serverless functions - not the version of the Satellite provided by Juno. + const extendedVersion = metadata?.juno?.functions?.version ?? metadata?.version; + const version = notEmptyString(extendedVersion) ? `version ${extendedVersion}, ` : ' '; + + console.log( + `${green('✔')} Build complete at ${formatTime()} (${version}esbuild ${esbuildVersion})` + ); + console.log(`→ ${yellow(key)} (${formatBytes(bytes)})`); +}; diff --git a/src/utils/pkg.utils.ts b/src/utils/pkg.utils.ts index 8e3edd23..341362f9 100644 --- a/src/utils/pkg.utils.ts +++ b/src/utils/pkg.utils.ts @@ -6,9 +6,11 @@ export const readPackageJson = async (): Promise => { const packageJson = await readFile(PACKAGE_JSON_PATH, 'utf-8'); // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - const {dependencies} = JSON.parse(packageJson) as {dependencies?: Record}; + const {dependencies, version, juno} = JSON.parse(packageJson) as PackageJson; return { - dependencies + dependencies, + version, + juno }; }; From 64ddb58b01aca519623573620be3469dc613be09 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Fri, 4 Apr 2025 11:35:44 +0200 Subject: [PATCH 7/7] feat: use utils --- package-lock.json | 14 +++++++------- package.json | 2 +- src/services/build/build.javascript.ts | 3 +-- src/types/pkg.ts | 13 ------------- src/utils/pkg.utils.ts | 17 +++-------------- 5 files changed, 12 insertions(+), 37 deletions(-) delete mode 100644 src/types/pkg.ts diff --git a/package-lock.json b/package-lock.json index aebb520c..89dd645a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@dfinity/identity": "^2.3.0", "@dfinity/principal": "^2.3.0", "@junobuild/admin": "^0.1.6", - "@junobuild/cli-tools": "^0.1.6", + "@junobuild/cli-tools": "^0.1.7", "@junobuild/config-loader": "^0.2.0", "@junobuild/core": "^0.1.9", "@junobuild/did-tools": "^0.2.0", @@ -1475,9 +1475,9 @@ } }, "node_modules/@junobuild/cli-tools": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@junobuild/cli-tools/-/cli-tools-0.1.6.tgz", - "integrity": "sha512-WN769rzl9effZ39Tmegi8F0qDOU04Ks1QXMG8Yh0D3wquynwHOVSDQjrRYCZeYIfwCnkkFPCpYaOmaUGSv9l5A==", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@junobuild/cli-tools/-/cli-tools-0.1.7.tgz", + "integrity": "sha512-URLlilOJ8f/cBsjRKTW80iaku316QAyxLXvjiCwNIsBo2Nc1z963qwFD2xy1zrq0cyqks4gTAy5L3E1+3QTxSQ==", "license": "MIT", "dependencies": { "file-type": "^20.4.1", @@ -7910,9 +7910,9 @@ "requires": {} }, "@junobuild/cli-tools": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@junobuild/cli-tools/-/cli-tools-0.1.6.tgz", - "integrity": "sha512-WN769rzl9effZ39Tmegi8F0qDOU04Ks1QXMG8Yh0D3wquynwHOVSDQjrRYCZeYIfwCnkkFPCpYaOmaUGSv9l5A==", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@junobuild/cli-tools/-/cli-tools-0.1.7.tgz", + "integrity": "sha512-URLlilOJ8f/cBsjRKTW80iaku316QAyxLXvjiCwNIsBo2Nc1z963qwFD2xy1zrq0cyqks4gTAy5L3E1+3QTxSQ==", "requires": { "file-type": "^20.4.1", "listr": "^0.14.3", diff --git a/package.json b/package.json index 9aa58560..e50102f2 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@dfinity/identity": "^2.3.0", "@dfinity/principal": "^2.3.0", "@junobuild/admin": "^0.1.6", - "@junobuild/cli-tools": "^0.1.6", + "@junobuild/cli-tools": "^0.1.7", "@junobuild/config-loader": "^0.2.0", "@junobuild/core": "^0.1.9", "@junobuild/did-tools": "^0.2.0", diff --git a/src/services/build/build.javascript.ts b/src/services/build/build.javascript.ts index 1c5e9f0c..d8878270 100644 --- a/src/services/build/build.javascript.ts +++ b/src/services/build/build.javascript.ts @@ -1,5 +1,5 @@ import {isEmptyString, isNullish, notEmptyString} from '@dfinity/utils'; -import {buildEsm, execute} from '@junobuild/cli-tools'; +import {buildEsm, execute, type PackageJson} from '@junobuild/cli-tools'; import type {Metafile} from 'esbuild'; import {green, magenta, red, yellow} from 'kleur'; import {existsSync} from 'node:fs'; @@ -15,7 +15,6 @@ import { PACKAGE_JSON_SPUTNIK_PATH } from '../../constants/dev.constants'; import type {BuildArgs, BuildLang} from '../../types/build'; -import type {PackageJson} from '../../types/pkg'; import {formatBytes, formatTime} from '../../utils/format.utils'; import {readPackageJson} from '../../utils/pkg.utils'; import {detectPackageManager} from '../../utils/pm.utils'; diff --git a/src/types/pkg.ts b/src/types/pkg.ts deleted file mode 100644 index 23209e4f..00000000 --- a/src/types/pkg.ts +++ /dev/null @@ -1,13 +0,0 @@ -export interface PackageJson { - dependencies?: Record; - version?: string; - juno?: PackageJsonJuno; -} - -export interface PackageJsonJuno { - functions?: PackageJsonJunoFunctions; -} - -export interface PackageJsonJunoFunctions { - version?: string; -} diff --git a/src/utils/pkg.utils.ts b/src/utils/pkg.utils.ts index 341362f9..01b4e0ed 100644 --- a/src/utils/pkg.utils.ts +++ b/src/utils/pkg.utils.ts @@ -1,16 +1,5 @@ -import {readFile} from 'node:fs/promises'; +import {type PackageJson, readPackageJson as readPackageJsonUtils} from '@junobuild/cli-tools'; import {PACKAGE_JSON_PATH} from '../constants/dev.constants'; -import type {PackageJson} from '../types/pkg'; -export const readPackageJson = async (): Promise => { - const packageJson = await readFile(PACKAGE_JSON_PATH, 'utf-8'); - - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - const {dependencies, version, juno} = JSON.parse(packageJson) as PackageJson; - - return { - dependencies, - version, - juno - }; -}; +export const readPackageJson = async (): Promise => + await readPackageJsonUtils({packageJsonPath: PACKAGE_JSON_PATH});