Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions src/constants/dev.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
100 changes: 91 additions & 9 deletions src/services/build/build.javascript.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import {buildEsm, execute} from '@junobuild/cli-tools';
import {isEmptyString, isNullish, notEmptyString} from '@dfinity/utils';
import {buildEsm, execute, type PackageJson} from '@junobuild/cli-tools';
import type {Metafile} from 'esbuild';
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';

Expand All @@ -29,10 +35,21 @@ const build = async (params: BuildArgsTsJs) => {

await createTargetDir();

await buildWithEsbuild(params);
const metadata = await prepareMetadata();

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<BuildResult> => {
const infile =
path ?? join(DEVELOPER_PROJECT_SATELLITE_PATH, lang === 'mjs' ? INDEX_MJS : INDEX_TS);

Expand Down Expand Up @@ -60,10 +77,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 () => {
Expand Down Expand Up @@ -98,3 +115,68 @@ const hasEsbuild = async (): Promise<boolean> => {
return false;
}
};

type BuildMetadata = Omit<PackageJson, 'dependencies'> | undefined;

const prepareMetadata = async (): Promise<BuildMetadata> => {
if (!existsSync(PACKAGE_JSON_PATH)) {
// No package.json therefore no metadata to pass to the build in the container.
return undefined;
}

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 undefined;
}

const functionsVersion = juno?.functions?.version;

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<void> => {
if (isNullish(metadata)) {
// No metadata to pass to the build in the container.
return;
}

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)})`);
};
20 changes: 4 additions & 16 deletions src/utils/pkg.utils.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
import {readFile} from 'node:fs/promises';
import {join} from 'node:path';
import {type PackageJson, readPackageJson as readPackageJsonUtils} from '@junobuild/cli-tools';
import {PACKAGE_JSON_PATH} from '../constants/dev.constants';

export interface PackageJson {
dependencies?: Record<string, string>;
}

export const readPackageJson = async (): Promise<PackageJson> => {
const packageJson = await readFile(join(process.cwd(), 'package.json'), 'utf-8');

// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const {dependencies} = JSON.parse(packageJson) as {dependencies?: Record<string, string>};

return {
dependencies
};
};
export const readPackageJson = async (): Promise<PackageJson> =>
await readPackageJsonUtils({packageJsonPath: PACKAGE_JSON_PATH});