Skip to content

refactors the experimental module-loader #237

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

Merged
merged 1 commit into from
Sep 20, 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
6 changes: 6 additions & 0 deletions .changeset/wise-turtles-rescue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@hypermod/fetcher': minor
'@hypermod/cli': minor
---

Refactors the module loader in order to support custom npm registries + auth keys.
2 changes: 1 addition & 1 deletion packages/cli/src/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { fetchPackages } from './utils/fetch-package';
import { getHypermodPackageName } from './utils/package-names';

export default async function list(packages: string[]) {
const packageManager = new PluginManager();
const packageManager = new PluginManager() as any;
const configs = [];

for (const packageName of packages) {
Expand Down
67 changes: 11 additions & 56 deletions packages/cli/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,68 +3,21 @@ import semver from 'semver';
import chalk from 'chalk';
import findUp from 'find-up';
import inquirer from 'inquirer';
import fs from 'fs-extra';
import { PluginManager, PluginManagerOptions } from 'live-plugin-manager';
import { installPackage } from '@antfu/install-pkg';

import * as core from '@hypermod/core';
import { fetchConfigAtPath } from '@hypermod/fetcher';
import {
type ModuleLoader as MdlLoader,
fetchConfigAtPath,
} from '@hypermod/fetcher';

import { InvalidUserInputError } from './errors';
import { fetchPackages } from './utils/fetch-package';
import { mergeConfigs } from './utils/merge-configs';
import { fetchConfigsForWorkspaces, getPackageJson } from './utils/file-system';
import ModuleLoader from './utils/module-loader';
import { getConfigPrompt, getMultiConfigPrompt } from './prompt';

const ExperimentalModuleLoader = () => {
const getInfo = (packageName: string) => {
const entryPath = require.resolve(packageName);
const location = entryPath.split(packageName)[0] + packageName;
const pkgJsonRaw = fs.readFileSync(
path.join(location, 'package.json'),
'utf8',
);
const pkgJson = JSON.parse(pkgJsonRaw);

return {
location,
entryPath,
pkgJson,
};
};

const install = async (packageName: string) => {
await installPackage(packageName, {
cwd: __dirname,
packageManager: 'npm',
additionalArgs: ['--force'],
});

const { pkgJson } = getInfo(packageName);

// Install whitelisted devDependencies
if (pkgJson?.hypermod?.dependencies) {
await Promise.all(
pkgJson.hypermod.dependencies.map((dep: string) => {
const version = pkgJson.devDependencies[dep];
if (!version) return;
return installPackage(`${dep}@${version}`, {
cwd: __dirname,
packageManager: 'npm',
additionalArgs: ['--force'],
});
}),
);
}
};

return {
install,
getInfo,
require: (packageName: string) => require(packageName),
};
};

export default async function main(
paths: string[],
flags: Partial<core.Flags>,
Expand All @@ -91,9 +44,12 @@ export default async function main(
};
}

const packageManager = flags.experimentalLoader
? ExperimentalModuleLoader()
: new PluginManager(pluginManagerConfig);
const packageManager: MdlLoader = flags.experimentalLoader
? ModuleLoader({
authToken: flags.registryToken,
npmRegistryUrl: flags.registry,
})
: (new PluginManager(pluginManagerConfig) as unknown as MdlLoader);

let transforms: string[] = [];

Expand Down Expand Up @@ -235,7 +191,6 @@ export default async function main(

const { community, remote } = await fetchPackages(
pkgName,
// @ts-expect-error Experimental loader
packageManager,
);

Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/utils/fetch-package.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import ora from 'ora';
import chalk from 'chalk';
import { PluginManager } from 'live-plugin-manager';

import {
fetchPackage,
fetchRemotePackage,
ConfigMeta,
type ModuleLoader,
} from '@hypermod/fetcher';
import { isValidConfig } from '@hypermod/validator';

import { getHypermodPackageName } from './package-names';

export async function fetchPackages(
packageName: string,
packageManager: PluginManager,
packageManager: ModuleLoader,
) {
const hypermodPackageName = getHypermodPackageName(packageName);
let hypermodPackage: ConfigMeta | undefined;
Expand Down
66 changes: 66 additions & 0 deletions packages/cli/src/utils/module-loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import path from 'path';
import fs from 'fs-extra';
import { installPackage } from '@antfu/install-pkg';

import { ModuleLoader } from '@hypermod/fetcher';

const ModuleLoader = (config: {
npmRegistryUrl?: string;
authToken?: string;
}): ModuleLoader => {
const getInfo = (packageName: string) => {
const entryPath = require.resolve(packageName);
const location = entryPath.split(packageName)[0] + packageName;
const pkgJsonRaw = fs.readFileSync(
path.join(location, 'package.json'),
'utf8',
);
const pkgJson = JSON.parse(pkgJsonRaw);

return {
location,
entryPath,
pkgJson,
};
};

const install = async (packageName: string) => {
await installPackage(packageName, {
cwd: __dirname,
packageManager: 'npm',
additionalArgs: [
'--force',
// --registry=https://your-custom-registry-url/ --//your-custom-registry-url/:_authToken=YOUR_AUTH_TOKEN
config.npmRegistryUrl ? `--registry=${config.npmRegistryUrl}` : '',
config.authToken
? `--${config.npmRegistryUrl}/:_authToken=${config.authToken}`
: '',
],
});

const { pkgJson } = getInfo(packageName);

// Install whitelisted devDependencies
if (pkgJson?.hypermod?.dependencies) {
await Promise.all(
pkgJson.hypermod.dependencies.map((dep: string) => {
const version = pkgJson.devDependencies[dep];
if (!version) return;
return installPackage(`${dep}@${version}`, {
cwd: __dirname,
packageManager: 'npm',
additionalArgs: ['--force'],
});
}),
);
}
};

return {
install,
getInfo,
require: (packageName: string) => require(packageName),
};
};

export default ModuleLoader;
60 changes: 27 additions & 33 deletions packages/fetcher/src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ jest.mock('globby');
import fs from 'fs';
import path from 'path';
import globby from 'globby';
import { PluginManager } from 'live-plugin-manager';

import { fetchConfig, fetchPackage, fetchRemotePackage } from '.';
import {
fetchConfig,
fetchPackage,
fetchRemotePackage,
type ModuleLoader,
} from '.';

const mockBasePath = path.join(__dirname, 'path', 'to');

Expand Down Expand Up @@ -109,28 +113,23 @@ describe('fetcher', () => {
require: jest.fn().mockReturnValue(mockConfig),
};

const configMeta = await fetchPackage(
'fake-package',
mockPackageManager as unknown as PluginManager,
);
const configMeta = await fetchPackage('fake-package', mockPackageManager);

expect(configMeta!.config).toEqual(mockConfig);
expect(configMeta!.filePath).toEqual(mockFilePath);
});

it('should throw if fetching fails', async () => {
const mockPackageManager = {
const mockPackageManager: ModuleLoader = {
install: jest.fn().mockRejectedValue('Import error'),
require: jest.fn().mockReturnValue(mockConfig),
getInfo: jest.fn(),
};

expect.assertions(1);

await expect(
fetchPackage(
'fake-package',
mockPackageManager as unknown as PluginManager,
),
fetchPackage('fake-package', mockPackageManager),
).rejects.toEqual('Import error');
});
});
Expand All @@ -150,7 +149,7 @@ describe('fetcher', () => {

const configMeta = await fetchRemotePackage(
'fake-package',
mockPackageManager as unknown as PluginManager,
mockPackageManager,
);

expect(configMeta!.config).toEqual(mockConfig);
Expand All @@ -160,17 +159,16 @@ describe('fetcher', () => {
});

it('should throw if fetching fails', async () => {
const mockPackageManager = {
const mockPackageManager: ModuleLoader = {
install: jest.fn().mockRejectedValue('Import error'),
getInfo: jest.fn(),
require: jest.fn(),
};

expect.assertions(1);

await expect(
fetchRemotePackage(
'fake-package',
mockPackageManager as unknown as PluginManager,
),
fetchRemotePackage('fake-package', mockPackageManager),
).rejects.toEqual('Import error');
});

Expand All @@ -185,7 +183,7 @@ describe('fetcher', () => {

const configMeta = await fetchRemotePackage(
'fake-package',
mockPackageManager as unknown as PluginManager,
mockPackageManager,
);

expect(configMeta!.config).toEqual(mockConfig);
Expand All @@ -205,42 +203,38 @@ describe('fetcher', () => {
Promise.resolve([]),
);

const res = await fetchRemotePackage(
'fake-package',
mockPackageManager as unknown as PluginManager,
);
const res = await fetchRemotePackage('fake-package', mockPackageManager);

expect(res).toBeUndefined();
});

it('should throw if fetching fails', async () => {
const mockPackageManager = {
const mockPackageManager: ModuleLoader = {
install: jest.fn().mockRejectedValue('Import error'),
getInfo: jest.fn(),
require: jest.fn(),
};

expect.assertions(1);

await expect(
fetchRemotePackage(
'fake-package',
mockPackageManager as unknown as PluginManager,
),
fetchRemotePackage('fake-package', mockPackageManager),
).rejects.toEqual('Import error');
});

it('should throw if package source cannot be retrieved', async () => {
const mockPackageManager = {
const mockPackageManager: ModuleLoader = {
install: jest.fn(),
getInfo: () => undefined,
getInfo: () => {
throw new Error('Package not found');
},
require: jest.fn(),
};

expect.assertions(1);

await expect(
fetchRemotePackage(
'fake-package',
mockPackageManager as unknown as PluginManager,
),
fetchRemotePackage('fake-package', mockPackageManager),
).rejects.toEqual(
new Error(`Unable to locate package files for package: 'fake-package'`),
);
Expand Down
Loading
Loading