Skip to content

Commit

Permalink
fix(*): fix crash on node v18.0 caused by Import Attribute syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
k-yle committed Dec 9, 2024
1 parent 4790a82 commit 48b36c4
Show file tree
Hide file tree
Showing 9 changed files with 36 additions and 10 deletions.
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'

0 comments on commit 48b36c4

Please sign in to comment.