Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support for juno.dev.config.ts|.js|.cjs|.mjs|.json #9

Merged
merged 3 commits into from
Feb 18, 2024
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
829 changes: 822 additions & 7 deletions cli/package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,17 @@
"lint": "eslint --max-warnings 0 src/**/*.ts"
},
"dependencies": {
"@babel/core": "^7.23.9",
"@babel/plugin-transform-modules-commonjs": "^7.23.3",
"@babel/preset-typescript": "^7.23.3",
"@dfinity/agent": "^0.20.2",
"@dfinity/candid": "^0.20.2",
"@dfinity/ic-management": "^2.2.1",
"@dfinity/identity": "^0.20.2",
"@dfinity/principal": "^0.20.2",
"@dfinity/utils": "^2.1.1",
"@junobuild/admin": "^0.0.45-next-2024-02-17.2",
"@junobuild/config": "^0.0.1-next-2024-02-17.1",
"@junobuild/config": "^0.0.1-next-2024-02-18",
"atomically": "^2.0.2",
"kleur": "^4.1.5",
"node-fetch": "^3.3.2",
Expand Down
8 changes: 5 additions & 3 deletions cli/src/commands/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import type {FileChangeInfo} from 'fs/promises';
import kleur from 'kleur';
import {existsSync} from 'node:fs';
import {watch as fsWatch} from 'node:fs/promises';
import {junoDevConfigExist, junoDevConfigFile} from '../configs/juno.dev.config';
import {DEV_DEPLOY_FOLDER, DEV_SATELLITE_WASM_FILENAME} from '../constants/constants';
import {initSatelliteModule, type SatelliteModule} from '../modules/satellite';
import {JUNO_DEV_CONFIG, configExist} from '../modules/satellite/satellite.config';
import {buildContext} from '../services/context.services';
import type {CliContext} from '../types/context';

Expand All @@ -16,7 +16,7 @@ export const watch = async (args?: string[]) => {
};

const watchConfig = async (args?: string[]) => {
if (!(await configExist())) {
if (!(await junoDevConfigExist())) {
console.log(`ℹ️ No configuration file provided. Watching for config updates skipped.`);
return;
}
Expand All @@ -25,7 +25,9 @@ const watchConfig = async (args?: string[]) => {

const context = await buildContext(args);

const watcher = fsWatch(JUNO_DEV_CONFIG);
const {configPath} = junoDevConfigFile();

const watcher = fsWatch(configPath);
for await (const $event of watcher) {
await onConfigFileWatch({$event, context});
}
Expand Down
96 changes: 96 additions & 0 deletions cli/src/configs/juno.dev.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import type {JunoDevConfig, JunoDevConfigFnOrObject} from '@junobuild/config';
import {existsSync} from 'node:fs';
import {access, readFile} from 'node:fs/promises';
import {join} from 'node:path';
import {JUNO_DEV_CONFIG_FILENAME, JUNO_DEV_JSON} from '../constants/constants';
import type {ConfigType} from '../types/config';
import {nodeRequire} from '../utils/node.utils';

export const junoDevConfigExist = async (): Promise<boolean> => {
try {
const {configPath} = junoDevConfigFile();
await access(configPath);
return true;
} catch (err: unknown) {
if (err instanceof Error && 'code' in err && (err as NodeJS.ErrnoException).code === 'ENOENT') {
return false;
} else {
throw err;
}
}
};

export const junoDevConfigFile = (): {configPath: string; configType: ConfigType} => {
const junoTs = join(process.cwd(), `${JUNO_DEV_CONFIG_FILENAME}.ts`);

if (existsSync(junoTs)) {
return {
configPath: junoTs,
configType: 'ts'
};
}

const junoJs = join(process.cwd(), `${JUNO_DEV_CONFIG_FILENAME}.js`);

if (existsSync(junoJs)) {
return {
configPath: junoJs,
configType: 'js'
};
}

const junoMjs = join(process.cwd(), `${JUNO_DEV_CONFIG_FILENAME}.mjs`);

if (existsSync(junoMjs)) {
return {
configPath: junoMjs,
configType: 'js'
};
}

const junoCjs = join(process.cwd(), `${JUNO_DEV_CONFIG_FILENAME}.cjs`);

if (existsSync(junoCjs)) {
return {
configPath: junoCjs,
configType: 'js'
};
}

// Support for original juno.json file
const junoJsonDeprecated = join(process.cwd(), JUNO_DEV_JSON);

if (existsSync(junoJsonDeprecated)) {
return {
configPath: junoJsonDeprecated,
configType: 'json'
};
}

return {
configPath: join(process.cwd(), `${JUNO_DEV_CONFIG_FILENAME}.json`),
configType: 'json'
};
};

export const readJunoDevConfig = async (): Promise<JunoDevConfig> => {
const {configPath, configType} = junoDevConfigFile();

const config = (userDevConfig: JunoDevConfigFnOrObject): JunoDevConfig =>
typeof userDevConfig === 'function' ? userDevConfig() : userDevConfig;

switch (configType) {
case 'ts': {
const {default: userDevConfig} = nodeRequire<JunoDevConfigFnOrObject>(configPath);
return config(userDevConfig);
}
case 'js': {
const {default: userDevConfig} = await import(configPath);
return config(userDevConfig as JunoDevConfigFnOrObject);
}
default: {
const buffer = await readFile(configPath);
return JSON.parse(buffer.toString('utf-8'));
}
}
};
6 changes: 6 additions & 0 deletions cli/src/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@ export const CLI_PROJECT_NAME = 'juno';
export const DEV_DEPLOY_FOLDER = join(process.cwd(), 'target', 'deploy');
export const DEV_SATELLITE_WASM_FILENAME = 'satellite.wasm.gz';
export const DEV_SATELLITE = join(DEV_DEPLOY_FOLDER, DEV_SATELLITE_WASM_FILENAME);

export const JUNO_DEV_CONFIG_FILENAME = 'juno.dev.config'; // .json | .js | .cjs | .mjs | .ts
/**
* @deprecated juno.dev.json is deprecated but still supported. We are now using juno.config.xxx
*/
export const JUNO_DEV_JSON = 'juno.dev.json';
5 changes: 3 additions & 2 deletions cli/src/modules/satellite/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {IDL} from '@dfinity/candid';
import {isNullish} from '@dfinity/utils';
import {existsSync} from 'node:fs';
import {junoDevConfigExist} from '../../configs/juno.dev.config';
import {DEV_SATELLITE} from '../../constants/constants';
import {Module} from '../../services/modules.services';
import {CliContext} from '../../types/context';
import {ModuleDescription, ModuleInstallParams} from '../../types/module';
import {configExist, configureCollections, configureControllers} from './satellite.config';
import {configureCollections, configureControllers} from './satellite.config';

export const SATELLITE: ModuleDescription = {
key: 'satellite',
Expand All @@ -28,7 +29,7 @@ export class SatelliteModule extends Module {
}

override async start(context: CliContext) {
if (!(await configExist())) {
if (!(await junoDevConfigExist())) {
console.log(`ℹ️ No configuration provided to configure ${this.name}.`);
return;
}
Expand Down
24 changes: 4 additions & 20 deletions cli/src/modules/satellite/satellite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,11 @@ import {
setSatelliteControllers
} from '@junobuild/admin';
import type {Controller} from '@junobuild/admin/declarations/satellite/satellite.did';
import type {
JunoDevConfig,
RulesType,
SatelliteDevCollection,
SatelliteDevController
} from '@junobuild/config';
import type {RulesType, SatelliteDevCollection, SatelliteDevController} from '@junobuild/config';
import fetch from 'node-fetch';
import {readFile} from 'node:fs/promises';
import {join} from 'node:path';
import {readJunoDevConfig} from '../../configs/juno.dev.config';
import type {CliContext} from '../../types/context';
import type {ModuleMetadata} from '../../types/module';
import {fileExist} from '../../utils/fs.utils';

export const JUNO_DEV_CONFIG = join(process.cwd(), 'juno.dev.json');

export const configExist = async (): Promise<boolean> => fileExist(JUNO_DEV_CONFIG);

const readConfig = async (): Promise<JunoDevConfig> => {
const buffer = await readFile(JUNO_DEV_CONFIG);
return JSON.parse(buffer.toString('utf-8'));
};

const list = async ({type, satellite}) =>
listRules({
Expand Down Expand Up @@ -85,7 +69,7 @@ export const configureCollections = async (context: SatelliteConfigContext) => {
satellite: {
collections: {db, storage}
}
} = await readConfig();
} = await readJunoDevConfig();

const satellite = buildSatelliteParams(context);

Expand All @@ -98,7 +82,7 @@ export const configureCollections = async (context: SatelliteConfigContext) => {
export const configureControllers = async (context: SatelliteConfigContext) => {
const {
satellite: {controllers}
} = await readConfig();
} = await readJunoDevConfig();

if ((controllers ?? []).length === 0) {
return;
Expand Down
1 change: 1 addition & 0 deletions cli/src/types/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type ConfigType = 'ts' | 'js' | 'json';
14 changes: 0 additions & 14 deletions cli/src/utils/fs.utils.ts

This file was deleted.

81 changes: 81 additions & 0 deletions cli/src/utils/node.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import {transformFileSync} from '@babel/core';
import * as mod from '@babel/plugin-transform-modules-commonjs';
import * as ts from '@babel/preset-typescript';
import {
defineConfig,
defineDevConfig,
type JunoConfig,
type JunoDevConfig
} from '@junobuild/config';
import {readFileSync} from 'node:fs';

/**
* Adapted source from Stencil (https://github.com/ionic-team/stencil/blob/main/src/compiler/sys/node-require.ts)
*
* Duplicate @junobuild/cli code. Someday we might want to either refactor the common CLIs' utilities or even merge both CLIs.
*/
export const nodeRequire = <T>(id: string): {default: T} => {
// ensure we cleared out node's internal require() cache for this file
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete require.cache[id];

// eslint-disable-next-line @typescript-eslint/no-var-requires
const Module = require('module');
const originalLoad = Module._load;

try {
// let's override node's require for a second
// don't worry, we'll revert this when we're done
// eslint-disable-next-line n/no-deprecated-api
require.extensions['.ts'] = (module: NodeJS.Module, fileName: string) => {
let sourceText = readFileSync(fileName, 'utf8');

if (fileName.endsWith('.ts')) {
// looks like we've got a typed config file
// let's transpile it to .js quick
sourceText = transformFileSync(fileName, {
presets: [ts.default],
plugins: [mod.default]
}).code;
} else {
// quick hack to turn a modern es module
// into and old school commonjs module
sourceText = sourceText.replace(/export\s+\w+\s+(\w+)/gm, 'exports.$1');
}

interface NodeModuleWithCompile extends NodeModule {
// eslint-disable-next-line @typescript-eslint/method-signature-style
_compile(code: string, filename: string): T;
}

// we need to coerce because of the requirements for the arguments to
// this function.
(module as NodeModuleWithCompile)._compile(sourceText, fileName);
};

// We override defineConfig because the library is unknown in the module we are trying to load.
// This need to be a function and not an arrow function because of the "arguments"
Module._load = function (request: string): unknown {
if (request === '@junobuild/config') {
return {
defineConfig: (config: JunoConfig) => defineConfig(config),
defineDevConfig: (config: JunoDevConfig) => defineDevConfig(config)
};
}

// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
// @ts-ignore arguments are passed by NodeJS
return originalLoad.apply(this, arguments);
};

// let's do this!
return require(id);
} finally {
// all set, let's go ahead and reset the require back to the default
// eslint-disable-next-line n/no-deprecated-api
require.extensions['.ts'] = undefined;

// Redo our hack
Module._load = originalLoad;
}
};
Loading