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

fix(*): fix crash on node v18.0 caused by Import Attribute syntax #395

Merged
merged 1 commit into from
Dec 11, 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
5 changes: 4 additions & 1 deletion dist/parse-args.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { createRequire } from 'node:module';
import { Command } from '@commander-js/extra-typings';
import { parseArgList } from './utils.js';
import { default as packageJson } from '../package.json' with { type: 'json' };
// TODO: once we drop support for node <v20.10, this can be converted to
// a normal import statement
const packageJson = createRequire(import.meta.url)('../package.json');
/**
* Parses the arguments passed into the cli
*/
Expand Down
4 changes: 2 additions & 2 deletions dist/parse-env-file.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { existsSync, readFileSync } from 'node:fs';
import { extname } from 'node:path';
import { pathToFileURL } from 'node:url';
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise } from './utils.js';
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise, importAttributesKeyword } from './utils.js';
/**
* Gets the environment vars from an env file
*/
Expand All @@ -19,7 +19,7 @@ export async function getEnvFileVars(envFilePath) {
// For some reason in ES Modules, only JSON file types need to be specifically delinated when importing them
let attributeTypes = {};
if (ext === '.json') {
attributeTypes = { with: { type: 'json' } };
attributeTypes = { [importAttributesKeyword]: { type: 'json' } };
}
const res = await import(pathToFileURL(absolutePath).href, attributeTypes);
if ('default' in res) {
Expand Down
4 changes: 2 additions & 2 deletions dist/parse-rc-file.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { stat, readFile } from 'node:fs';
import { promisify } from 'node:util';
import { extname } from 'node:path';
import { pathToFileURL } from 'node:url';
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise } from './utils.js';
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise, importAttributesKeyword } from './utils.js';
const statAsync = promisify(stat);
const readFileAsync = promisify(readFile);
/**
Expand All @@ -26,7 +26,7 @@ export async function getRCFileVars({ environments, filePath }) {
// For some reason in ES Modules, only JSON file types need to be specifically delinated when importing them
let attributeTypes = {};
if (ext === '.json') {
attributeTypes = { with: { type: 'json' } };
attributeTypes = { [importAttributesKeyword]: { type: 'json' } };
}
const res = await import(pathToFileURL(absolutePath).href, attributeTypes);
if ('default' in res) {
Expand Down
1 change: 1 addition & 0 deletions dist/utils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export declare function parseArgList(list: string): string[];
* A simple function to test if the value is a promise/thenable
*/
export declare function isPromise<T>(value?: T | PromiseLike<T>): value is PromiseLike<T>;
export declare const importAttributesKeyword: string;
6 changes: 6 additions & 0 deletions dist/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,9 @@ export function isPromise(value) {
&& 'then' in value
&& typeof value.then === 'function';
}
// "Import Attributes" are only supported since node v18.20 and v20.10.
// For older node versions, we have to use "Import Assertions".
// TODO: remove this check when we drop support for node v20
const [major, minor] = process.version.slice(1).split('.').map(Number);
const legacyImportAssertions = (major === 18 && minor < 20) || (major === 20 && minor < 10);
export const importAttributesKeyword = legacyImportAssertions ? 'assert' : 'with';
8 changes: 7 additions & 1 deletion src/parse-args.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { createRequire } from 'node:module'
import { Command } from '@commander-js/extra-typings'
import type { EnvCmdOptions, CommanderOptions, EnvFileOptions, RCFileOptions } from './types.ts'
import { parseArgList } from './utils.js'
import { default as packageJson } from '../package.json' with { type: 'json' };

// TODO: once we drop support for node <v20.10, this can be converted to
// a normal import statement
const packageJson = createRequire(import.meta.url)('../package.json') as {
version: string;
}

/**
* Parses the arguments passed into the cli
Expand Down
4 changes: 2 additions & 2 deletions src/parse-env-file.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { existsSync, readFileSync } from 'node:fs'
import { extname } from 'node:path'
import { pathToFileURL } from 'node:url'
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise } from './utils.js'
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise, importAttributesKeyword } from './utils.js'
import type { Environment } from './types.ts'

/**
Expand All @@ -22,7 +22,7 @@ export async function getEnvFileVars(envFilePath: string): Promise<Environment>
// For some reason in ES Modules, only JSON file types need to be specifically delinated when importing them
let attributeTypes = {}
if (ext === '.json') {
attributeTypes = { with: { type: 'json' } }
attributeTypes = { [importAttributesKeyword]: { type: 'json' } }
}
const res = await import(pathToFileURL(absolutePath).href, attributeTypes) as Environment | { default: Environment }
if ('default' in res) {
Expand Down
4 changes: 2 additions & 2 deletions src/parse-rc-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { stat, readFile } from 'node:fs'
import { promisify } from 'node:util'
import { extname } from 'node:path'
import { pathToFileURL } from 'node:url'
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise } from './utils.js'
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise, importAttributesKeyword } from './utils.js'
import type { Environment, RCEnvironment } from './types.ts'

const statAsync = promisify(stat)
Expand Down Expand Up @@ -33,7 +33,7 @@ export async function getRCFileVars(
// For some reason in ES Modules, only JSON file types need to be specifically delinated when importing them
let attributeTypes = {}
if (ext === '.json') {
attributeTypes = { with: { type: 'json' } }
attributeTypes = { [importAttributesKeyword]: { type: 'json' } }
}
const res = await import(pathToFileURL(absolutePath).href, attributeTypes) as RCEnvironment | { default: RCEnvironment }
if ('default' in res) {
Expand Down
10 changes: 10 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,13 @@ export function isPromise<T>(value?: T | PromiseLike<T>): value is PromiseLike<T
&& 'then' in value
&& typeof value.then === 'function'
}


// "Import Attributes" are only supported since node v18.20 and v20.10.
// For older node versions, we have to use "Import Assertions".
// TODO: remove this check when we drop support for node v20
const [major, minor] = process.version.slice(1).split('.').map(Number)
const legacyImportAssertions =
(major === 18 && minor < 20) || (major === 20 && minor < 10)

export const importAttributesKeyword = legacyImportAssertions ? 'assert' : 'with'
Loading