Skip to content

Commit

Permalink
detect package manager when spawning dmno dev for config loader client (
Browse files Browse the repository at this point in the history
  • Loading branch information
theoephraim authored May 28, 2024
1 parent 33ad7d6 commit cff6475
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 76 deletions.
6 changes: 6 additions & 0 deletions .changeset/rich-cameras-punch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@dmno/astro-integration": patch
"dmno": patch
---

spawn dmno dev using correct package manager, astro integration cleanup, dynamic item prerender warning
1 change: 1 addition & 0 deletions example-repo/packages/astro-web/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
"compilerOptions": {
"jsx": "preserve"
},
"exclude": [ "dist", "~partytown" ]
}
1 change: 0 additions & 1 deletion packages/core/src/app-init/inject-dmno-globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ export function injectDmnoGlobals(
opts?: {
injectedConfig?: InjectedDmnoEnv,
trackingObject?: Record<string, boolean>,
dynamicAccessDisabled?: boolean,
onItemAccess?: (item: InjectedDmnoEnvItem) => void;
},
) {
Expand Down
10 changes: 7 additions & 3 deletions packages/core/src/config-loader/config-server-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ConfigLoaderRequestMap } from './ipc-requests';
import { SerializedService } from './serialization-types';
import { formatError, getItemSummary } from '../cli/lib/formatting';
import { InjectedDmnoEnv } from '../config-engine/config-engine';
import { detectPackageManagerSync } from '../lib/detect-package-manager';

const debug = Debug('dmno');
const debugTimer = createDebugTimer('dmno:loader-client');
Expand Down Expand Up @@ -50,7 +51,10 @@ export class ConfigServerClient {

private ownedDmnoConfigServerProcess?: ChildProcess;
private initOwnedConfigServer() {
this.ownedDmnoConfigServerProcess = spawn('pnpm', 'exec dmno dev --silent'.split(' '), {
const { packageManager } = detectPackageManagerSync();

// use `pnpm exec` or `npm exec` etc...
this.ownedDmnoConfigServerProcess = spawn(packageManager, 'exec -- dmno dev --silent'.split(' '), {
stdio: 'inherit',
env: {
...process.env,
Expand Down Expand Up @@ -85,7 +89,7 @@ export class ConfigServerClient {
});

this.ownedDmnoConfigServerProcess.on('exit', (code, signal) => {
console.log('dmno config server process exit', code, signal);
// console.log('dmno config server process exit', code, signal);
if (!this.isShuttingDown) process.exit(code || 1);
});
}
Expand Down Expand Up @@ -118,7 +122,7 @@ export class ConfigServerClient {
});

this.ipc.of.dmno.on('event', (eventMessage) => {
console.log('received IPC event message', eventMessage);
// console.log('received IPC event message', eventMessage);
this.eventBus.emit(eventMessage.type, eventMessage.payload);
});

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/config-loader/config-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export class ConfigServer {

eventBus = mitt();
onEvent(eventType: string, handler: Handler) {
console.log('loader process subscribe to event', eventType, handler);
// console.log('loader process subscribe to event', eventType, handler);
this.eventBus.on(eventType, handler);
}

Expand All @@ -136,7 +136,7 @@ export class ConfigServer {

onReload?: () => void;
private onConfigReload() {
this.broadcastIpcEvent('reload', { foo: 1 });
this.broadcastIpcEvent('reload', {});
if (this.onReload) this.onReload();
}

Expand Down
58 changes: 3 additions & 55 deletions packages/core/src/config-loader/find-services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import { fdir } from 'fdir';
import { tryCatch } from '@dmno/ts-lib';
import Debug from 'debug';
import { asyncMapValues } from '../lib/async-utils';
import { PackageManager, detectPackageManager } from '../lib/detect-package-manager';

const debug = Debug('dmno:find-services');

export async function readJsonFile(path: string) {
return JSON.parse(await fs.promises.readFile(path, 'utf8'));
}

export type PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun' | 'moon';

export type WorkspacePackagesListing = {
name: string,
version?: string,
Expand Down Expand Up @@ -42,61 +43,8 @@ export async function pathExists(p: string) {

export async function findDmnoServices(includeUnitialized = true): Promise<ScannedWorkspaceInfo> {
const startAt = new Date();
let cwd = process.cwd();
debug(`begin scan for services from ${cwd}`);

const cwdParts = cwd.split('/');

let packageManager: PackageManager | undefined;
let possibleRootPackage: string | undefined;

while (!packageManager) {
// we could also try to detect the current package manager via env vars (ex: process.env.PNPM_PACKAGE_NAME)
// and then not check for all of the lockfiles...?

// TODO: nx and lerna support? (lerna.json has packages array)
// TODO: deno?

const filesFound = await asyncMapValues({
packageJson: 'package.json',
yarnLock: 'yarn.lock',
npmLock: 'package-lock.json',
pnpmLock: 'pnpm-lock.yaml',
pnpmWorkspace: 'pnpm-workspace.yaml',
bunLock: 'bun.lockb',
moonWorkspace: '.moon/workspace.yml',
// eslint-disable-next-line @typescript-eslint/no-loop-func
}, async (filePath) => pathExists(path.resolve(cwd, filePath)));

if (filesFound.packageJson) possibleRootPackage = cwd;

if (filesFound.pnpmLock || filesFound.pnpmWorkspace) packageManager = 'pnpm';
else if (filesFound.npmLock) packageManager = 'npm';
else if (filesFound.yarnLock) packageManager = 'yarn';
else if (filesFound.bunLock) packageManager = 'bun';
else if (filesFound.moonWorkspace) packageManager = 'moon';

if (!packageManager) {
cwdParts.pop();
cwd = cwdParts.join('/');
}
// show some hopefully useful error messaging if we hit the root folder without finding anything
if (cwd === '') {
console.log(kleur.red('Unable to find detect your package manager and workspace root!'));
if (possibleRootPackage) {
console.log(`But it looks like your workspace root might be ${kleur.green().italic(possibleRootPackage)}`);
}
console.log('We look for lock files (ex: package-lock.json) so you may just need to run a dependency install (ie `npm install`)');
process.exit(1);
}
}
const rootServicePath = cwd;

debug('finished scanning for workspace root', {
packageManager,
rootServicePath,
});

const { packageManager, rootWorkspacePath: rootServicePath } = await detectPackageManager();

let packagePatterns: Array<string> | undefined;
let isMonorepo = false;
Expand Down
138 changes: 138 additions & 0 deletions packages/core/src/lib/detect-package-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import fs from 'fs';
import path from 'path';
import kleur from 'kleur';
import { asyncMapValues } from './async-utils';

export type PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun' | 'moon';


export async function pathExists(p: string) {
try {
await fs.promises.access(p);
return true;
} catch {
return false;
}
}
function pathExistsSync(p:string) {
try {
fs.accessSync(p);
return true;
} catch {
return false;
}
}

// TODO: nx and lerna support? (lerna.json has packages array)
// TODO: deno?
const PACKAGE_MANAGER_RELEVANT_FILES = {
packageJson: 'package.json',
yarnLock: 'yarn.lock',
npmLock: 'package-lock.json',
pnpmLock: 'pnpm-lock.yaml',
pnpmWorkspace: 'pnpm-workspace.yaml',
bunLock: 'bun.lockb',
moonWorkspace: '.moon/workspace.yml',
};

// SEE SYNC VERSION BELOW - UPDATE BOTH IF ANY CHANGES ARE MADE!
export async function detectPackageManager() {
let cwd = process.cwd();

const cwdParts = cwd.split('/');

let packageManager: PackageManager | undefined;
let possibleRootPackage: string | undefined;

while (!packageManager) {
// we could also try to detect the current package manager via env vars (ex: process.env.PNPM_PACKAGE_NAME)
// and then not check for all of the lockfiles...?


const filesFound = await asyncMapValues(
PACKAGE_MANAGER_RELEVANT_FILES,
// eslint-disable-next-line @typescript-eslint/no-loop-func
async (filePath) => pathExists(path.resolve(cwd, filePath)),
);

if (filesFound.packageJson) possibleRootPackage = cwd;

if (filesFound.pnpmLock || filesFound.pnpmWorkspace) packageManager = 'pnpm';
else if (filesFound.npmLock) packageManager = 'npm';
else if (filesFound.yarnLock) packageManager = 'yarn';
else if (filesFound.bunLock) packageManager = 'bun';
else if (filesFound.moonWorkspace) packageManager = 'moon';

if (!packageManager) {
cwdParts.pop();
cwd = cwdParts.join('/');
}
// show some hopefully useful error messaging if we hit the root folder without finding anything
if (cwd === '') {
console.log(kleur.red('Unable to find detect your package manager and workspace root!'));
if (possibleRootPackage) {
console.log(`But it looks like your workspace root might be ${kleur.green().italic(possibleRootPackage)}`);
}
console.log('We look for lock files (ex: package-lock.json) so you may just need to run a dependency install (ie `npm install`)');
process.exit(1);
}
}

return {
packageManager,
rootWorkspacePath: cwd,
};
}


// sync version of above fn, probably dont want this... but fine for now
export function detectPackageManagerSync() {
let cwd = process.cwd();

const cwdParts = cwd.split('/');

let packageManager: PackageManager | undefined;
let possibleRootPackage: string | undefined;

while (!packageManager) {
// we could also try to detect the current package manager via env vars (ex: process.env.PNPM_PACKAGE_NAME)
// and then not check for all of the lockfiles...?

const filesFound: Partial<Record<keyof typeof PACKAGE_MANAGER_RELEVANT_FILES, boolean>> = {};
for (const fileKey of Object.keys(PACKAGE_MANAGER_RELEVANT_FILES)) {
const key = fileKey as keyof typeof PACKAGE_MANAGER_RELEVANT_FILES;
const filePath = path.resolve(cwd, PACKAGE_MANAGER_RELEVANT_FILES[key]);
filesFound[key] = pathExistsSync(filePath);
}

if (filesFound.packageJson) possibleRootPackage = cwd;

if (filesFound.pnpmLock || filesFound.pnpmWorkspace) packageManager = 'pnpm';
else if (filesFound.npmLock) packageManager = 'npm';
else if (filesFound.yarnLock) packageManager = 'yarn';
else if (filesFound.bunLock) packageManager = 'bun';
else if (filesFound.moonWorkspace) packageManager = 'moon';

if (!packageManager) {
cwdParts.pop();
cwd = cwdParts.join('/');
}
// show some hopefully useful error messaging if we hit the root folder without finding anything
if (cwd === '') {
console.log(kleur.red('Unable to find detect your package manager and workspace root!'));
if (possibleRootPackage) {
console.log(`But it looks like your workspace root might be ${kleur.green().italic(possibleRootPackage)}`);
}
console.log('We look for lock files (ex: package-lock.json) so you may just need to run a dependency install (ie `npm install`)');
process.exit(1);
}
}

return {
packageManager,
rootWorkspacePath: cwd,
};
}



2 changes: 1 addition & 1 deletion packages/integrations/astro/src/astro-middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ for (const itemKey of sensitiveItemKeys) {


export const onRequest: MiddlewareHandler = async (context, next) => {
console.log(`custom astro middleware executed - ${context.url}`);
// console.log(`custom astro middleware executed - ${context.url}`);

const response = await next();

Expand Down
26 changes: 12 additions & 14 deletions packages/integrations/astro/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ async function reloadDmnoConfig() {
injectDmnoGlobals({
injectedConfig: serializedServiceToInjectedConfig(dmnoService),
trackingObject: configItemKeysAccessed,
dynamicAccessDisabled: true,
});
publicDynamicItemKeys = (globalThis as any)._DMNO_PUBLIC_DYNAMIC_KEYS;
sensitiveItemKeys = (globalThis as any)._DMNO_SENSITIVE_KEYS;
Expand All @@ -47,8 +46,6 @@ let astroCommand: 'dev' | 'build' | 'preview' = 'dev';





let dmnoHasTriggeredReload = false;

type DmnoAstroIntegrationOptions = {
Expand Down Expand Up @@ -124,6 +121,7 @@ function dmnoAstroIntegration(dmnoIntegrationOpts?: DmnoAstroIntegrationOptions)
// console.log('astro vite plugin configure server');
if (!isRestart) {
dmnoConfigClient.eventBus.on('reload', () => {
opts.logger.info('💫 dmno config updated - restarting astro server');
// eslint-disable-next-line @typescript-eslint/no-floating-promises
server.restart();
dmnoHasTriggeredReload = true;
Expand Down Expand Up @@ -245,17 +243,17 @@ function dmnoAstroIntegration(dmnoIntegrationOpts?: DmnoAstroIntegrationOptions)
// otherwise, we want to check which config was used during prerendering
// so if any were expected to be dyanmic (ie loaded at boot time) we can throw/warn

let dynamicConfigPrerendered = false;
for (const itemKey in configItemKeysAccessed) {
const configItem = dmnoService.config[itemKey];
if (configItem.isDynamic) {
dynamicConfigPrerendered = true;
opts.logger.error(`Dynamic config item "${itemKey}" was used during pre-render`);
opts.logger.error('> Change to `{ "dynamic": "false" }` to make it static');
}
}
if (dynamicConfigPrerendered) {
throw new Error('Dynamic config used during static pre-rendering');
// TODO: currently we're just showing a warning, may want to throw? have more settings?
const dynamicKeysUsedDuringPrerender = Object.keys(configItemKeysAccessed)
.filter((k) => dmnoService.config[k].isDynamic);
if (dynamicKeysUsedDuringPrerender.length) {
opts.logger.warn('Dynamic config items were accessed during pre-render:');
dynamicKeysUsedDuringPrerender.forEach((k) => {
opts.logger.warn(`- ${k}`);
});
opts.logger.warn('> Change service\'s default behavior by adjusting `settings.dynamicConfig`');
opts.logger.warn('> Or adjust individual items to `{ "dynamic": "false" }` to make them static');
opts.logger.warn('> See https://dmno.dev/docs/guides/dynamic-config/ for more info');
}
},
},
Expand Down

0 comments on commit cff6475

Please # to comment.