fix(typescript): load config written in TypeScript without the typescript package when using Bun, Deno or tsx
This commit is contained in:
parent
198aa545eb
commit
c838ffb7f3
9 changed files with 132 additions and 32 deletions
5
.changeset/breezy-sheep-yawn.md
Normal file
5
.changeset/breezy-sheep-yawn.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@emigrate/cli': minor
|
||||
---
|
||||
|
||||
Make it possible to write the Emigrate configuration file in TypeScript and load it using `tsx` in a NodeJS environment by importing packages provided using the `--import` CLI option before loading the configuration file. This makes it possible to run Emigrate in production with a configuration file written in TypeScript without having the `typescript` package installed.
|
||||
5
.changeset/cool-spies-behave.md
Normal file
5
.changeset/cool-spies-behave.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@emigrate/docs': patch
|
||||
---
|
||||
|
||||
Add note on how to write Emigrate's config using TypeScript in a production environment without having `typescript` installed.
|
||||
5
.changeset/tidy-shrimps-hide.md
Normal file
5
.changeset/tidy-shrimps-hide.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@emigrate/cli': patch
|
||||
---
|
||||
|
||||
Don't use the `typescript` package for loading an Emigrate configuration file written in TypeScript in a Bun or Deno environment
|
||||
|
|
@ -7,14 +7,14 @@ import { Tabs, TabItem } from '@astrojs/starlight/components';
|
|||
import Link from '@components/Link.astro';
|
||||
|
||||
:::tip[Using Bun or Deno?]
|
||||
If you are using [Bun](https://bun.sh) or [Deno](https://deno.land) you are already good to go as they both support TypeScript out of the box.
|
||||
If you are using [Bun](https://bun.sh) or [Deno](https://deno.land) you are already good to go as they both support TypeScript out of the box!
|
||||
:::
|
||||
|
||||
You have at least the two following options to support running TypeScript migration files in NodeJS.
|
||||
If you're using NodeJS you have at least the two following options to support running TypeScript migration files in NodeJS.
|
||||
|
||||
## Using `tsx`
|
||||
|
||||
If you want to be able to write and run migration files written in TypeScript the easiest way is to install the [`tsx`](https://github.com/privatenumber/tsx) package.
|
||||
If you want to be able to write and run migration files written in TypeScript an easy way is to install the [`tsx`](https://github.com/privatenumber/tsx) package.
|
||||
|
||||
### Installing `tsx`
|
||||
|
||||
|
|
@ -67,9 +67,13 @@ Using the <Link href="/commands/up/#-i---import-module">`--import`</Link> flag y
|
|||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::note
|
||||
This method is necessary if you want to write your configuration file in TypeScript without having `typescript` installed in your production environment, as `tsx` must be loaded before the configuration file is loaded.
|
||||
:::
|
||||
|
||||
#### Via configuration file
|
||||
|
||||
You can also directly import `tsx` in your configuration file.
|
||||
You can also directly import `tsx` in your configuration file (will only work if you're not using TypeScript for your configuration file).
|
||||
|
||||
```js title="emigrate.config.js" {1}
|
||||
import 'tsx';
|
||||
|
|
|
|||
|
|
@ -36,7 +36,9 @@
|
|||
"immigration"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@emigrate/tsconfig": "workspace:*"
|
||||
"@emigrate/tsconfig": "workspace:*",
|
||||
"@types/bun": "1.0.5",
|
||||
"bun-types": "1.0.26"
|
||||
},
|
||||
"author": "Aboviq AB <dev@aboviq.com> (https://www.aboviq.com)",
|
||||
"homepage": "https://github.com/aboviq/emigrate/tree/main/packages/cli#readme",
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ const importAll = async (cwd: string, modules: string[]) => {
|
|||
};
|
||||
|
||||
const up: Action = async (args, abortSignal) => {
|
||||
const config = await getConfig('up');
|
||||
const { values } = parseArgs({
|
||||
args,
|
||||
options: {
|
||||
|
|
@ -143,6 +142,14 @@ Examples:
|
|||
}
|
||||
|
||||
const cwd = process.cwd();
|
||||
|
||||
if (values.import) {
|
||||
await importAll(cwd, values.import);
|
||||
}
|
||||
|
||||
const forceImportTypeScriptAsIs = values.import?.some((module) => module === 'tsx' || module.startsWith('tsx/'));
|
||||
|
||||
const config = await getConfig('up', forceImportTypeScriptAsIs);
|
||||
const {
|
||||
directory = config.directory,
|
||||
storage = config.storage,
|
||||
|
|
@ -151,7 +158,6 @@ Examples:
|
|||
from,
|
||||
to,
|
||||
limit: limitString,
|
||||
import: imports = [],
|
||||
'abort-respite': abortRespiteString,
|
||||
'no-execution': noExecution,
|
||||
} = values;
|
||||
|
|
@ -177,8 +183,6 @@ Examples:
|
|||
return;
|
||||
}
|
||||
|
||||
await importAll(cwd, imports);
|
||||
|
||||
try {
|
||||
const { default: upCommand } = await import('./commands/up.js');
|
||||
process.exitCode = await upCommand({
|
||||
|
|
@ -209,7 +213,6 @@ Examples:
|
|||
};
|
||||
|
||||
const newMigration: Action = async (args) => {
|
||||
const config = await getConfig('new');
|
||||
const { values, positionals } = parseArgs({
|
||||
args,
|
||||
options: {
|
||||
|
|
@ -239,6 +242,12 @@ const newMigration: Action = async (args) => {
|
|||
multiple: true,
|
||||
default: [],
|
||||
},
|
||||
import: {
|
||||
type: 'string',
|
||||
short: 'i',
|
||||
multiple: true,
|
||||
default: [],
|
||||
},
|
||||
color: {
|
||||
type: 'boolean',
|
||||
},
|
||||
|
|
@ -260,14 +269,24 @@ Arguments:
|
|||
Options:
|
||||
|
||||
-h, --help Show this help message and exit
|
||||
|
||||
-d, --directory <path> The directory where the migration files are located (required)
|
||||
|
||||
-i, --import <module> Additional modules/packages to import before creating the migration (can be specified multiple times)
|
||||
For example if you want to use Dotenv to load environment variables or when using TypeScript
|
||||
|
||||
-r, --reporter <name> The reporter to use for reporting the migration file creation progress (default: pretty)
|
||||
|
||||
-p, --plugin <name> The plugin(s) to use (can be specified multiple times)
|
||||
|
||||
-t, --template <path> A template file to use as contents for the new migration file
|
||||
(if the extension option is not provided the template file's extension will be used)
|
||||
|
||||
-x, --extension <ext> The extension to use for the new migration file
|
||||
(if no template or plugin is provided an empty migration file will be created with the given extension)
|
||||
|
||||
--color Force color output (this option is passed to the reporter)
|
||||
|
||||
--no-color Disable color output (this option is passed to the reporter)
|
||||
|
||||
One of the --template, --extension or the --plugin options must be specified
|
||||
|
|
@ -287,6 +306,14 @@ Examples:
|
|||
}
|
||||
|
||||
const cwd = process.cwd();
|
||||
|
||||
if (values.import) {
|
||||
await importAll(cwd, values.import);
|
||||
}
|
||||
|
||||
const forceImportTypeScriptAsIs = values.import?.some((module) => module === 'tsx' || module.startsWith('tsx/'));
|
||||
|
||||
const config = await getConfig('new', forceImportTypeScriptAsIs);
|
||||
const {
|
||||
directory = config.directory,
|
||||
template = config.template,
|
||||
|
|
@ -312,7 +339,6 @@ Examples:
|
|||
};
|
||||
|
||||
const list: Action = async (args) => {
|
||||
const config = await getConfig('list');
|
||||
const { values } = parseArgs({
|
||||
args,
|
||||
options: {
|
||||
|
|
@ -355,12 +381,18 @@ List all migrations and their status. This command does not run any migrations.
|
|||
Options:
|
||||
|
||||
-h, --help Show this help message and exit
|
||||
|
||||
-d, --directory <path> The directory where the migration files are located (required)
|
||||
|
||||
-i, --import <module> Additional modules/packages to import before listing the migrations (can be specified multiple times)
|
||||
For example if you want to use Dotenv to load environment variables
|
||||
|
||||
-r, --reporter <name> The reporter to use for reporting the migrations (default: pretty)
|
||||
|
||||
-s, --storage <name> The storage to use to get the migration history (required)
|
||||
|
||||
--color Force color output (this option is passed to the reporter)
|
||||
|
||||
--no-color Disable color output (this option is passed to the reporter)
|
||||
|
||||
Examples:
|
||||
|
|
@ -376,14 +408,15 @@ Examples:
|
|||
}
|
||||
|
||||
const cwd = process.cwd();
|
||||
const {
|
||||
directory = config.directory,
|
||||
storage = config.storage,
|
||||
reporter = config.reporter,
|
||||
import: imports = [],
|
||||
} = values;
|
||||
|
||||
await importAll(cwd, imports);
|
||||
if (values.import) {
|
||||
await importAll(cwd, values.import);
|
||||
}
|
||||
|
||||
const forceImportTypeScriptAsIs = values.import?.some((module) => module === 'tsx' || module.startsWith('tsx/'));
|
||||
|
||||
const config = await getConfig('list', forceImportTypeScriptAsIs);
|
||||
const { directory = config.directory, storage = config.storage, reporter = config.reporter } = values;
|
||||
|
||||
try {
|
||||
const { default: listCommand } = await import('./commands/list.js');
|
||||
|
|
@ -401,7 +434,6 @@ Examples:
|
|||
};
|
||||
|
||||
const remove: Action = async (args) => {
|
||||
const config = await getConfig('remove');
|
||||
const { values, positionals } = parseArgs({
|
||||
args,
|
||||
options: {
|
||||
|
|
@ -453,13 +485,20 @@ Arguments:
|
|||
Options:
|
||||
|
||||
-h, --help Show this help message and exit
|
||||
|
||||
-d, --directory <path> The directory where the migration files are located (required)
|
||||
|
||||
-i, --import <module> Additional modules/packages to import before removing the migration (can be specified multiple times)
|
||||
For example if you want to use Dotenv to load environment variables
|
||||
|
||||
-r, --reporter <name> The reporter to use for reporting the removal process (default: pretty)
|
||||
|
||||
-s, --storage <name> The storage to use to get the migration history (required)
|
||||
|
||||
-f, --force Force removal of the migration history entry even if the migration is not in a failed state
|
||||
|
||||
--color Force color output (this option is passed to the reporter)
|
||||
|
||||
--no-color Disable color output (this option is passed to the reporter)
|
||||
|
||||
Examples:
|
||||
|
|
@ -477,15 +516,15 @@ Examples:
|
|||
}
|
||||
|
||||
const cwd = process.cwd();
|
||||
const {
|
||||
directory = config.directory,
|
||||
storage = config.storage,
|
||||
reporter = config.reporter,
|
||||
force,
|
||||
import: imports = [],
|
||||
} = values;
|
||||
|
||||
await importAll(cwd, imports);
|
||||
if (values.import) {
|
||||
await importAll(cwd, values.import);
|
||||
}
|
||||
|
||||
const forceImportTypeScriptAsIs = values.import?.some((module) => module === 'tsx' || module.startsWith('tsx/'));
|
||||
|
||||
const config = await getConfig('remove', forceImportTypeScriptAsIs);
|
||||
const { directory = config.directory, storage = config.storage, reporter = config.reporter, force } = values;
|
||||
|
||||
try {
|
||||
const { default: removeCommand } = await import('./commands/remove.js');
|
||||
|
|
|
|||
6
packages/cli/src/deno.d.ts
vendored
Normal file
6
packages/cli/src/deno.d.ts
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const Deno: any;
|
||||
}
|
||||
|
||||
export {};
|
||||
|
|
@ -1,11 +1,16 @@
|
|||
import { cosmiconfig } from 'cosmiconfig';
|
||||
import process from 'node:process';
|
||||
import { cosmiconfig, defaultLoaders } from 'cosmiconfig';
|
||||
import { type Config, type EmigrateConfig } from './types.js';
|
||||
|
||||
const commands = ['up', 'list', 'new', 'remove'] as const;
|
||||
type Command = (typeof commands)[number];
|
||||
const canImportTypeScriptAsIs = Boolean(process.isBun) || typeof Deno !== 'undefined';
|
||||
|
||||
export const getConfig = async (command: Command): Promise<Config> => {
|
||||
const explorer = cosmiconfig('emigrate');
|
||||
export const getConfig = async (command: Command, forceImportTypeScriptAsIs = false): Promise<Config> => {
|
||||
const explorer = cosmiconfig('emigrate', {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
loaders: forceImportTypeScriptAsIs || canImportTypeScriptAsIs ? { '.ts': defaultLoaders['.js'] } : undefined,
|
||||
});
|
||||
|
||||
const result = await explorer.search();
|
||||
|
||||
|
|
|
|||
33
pnpm-lock.yaml
generated
33
pnpm-lock.yaml
generated
|
|
@ -108,6 +108,12 @@ importers:
|
|||
'@emigrate/tsconfig':
|
||||
specifier: workspace:*
|
||||
version: link:../tsconfig
|
||||
'@types/bun':
|
||||
specifier: 1.0.5
|
||||
version: 1.0.5
|
||||
bun-types:
|
||||
specifier: 1.0.26
|
||||
version: 1.0.26
|
||||
|
||||
packages/mysql:
|
||||
dependencies:
|
||||
|
|
@ -1759,6 +1765,12 @@ packages:
|
|||
'@babel/types': 7.23.6
|
||||
dev: false
|
||||
|
||||
/@types/bun@1.0.5:
|
||||
resolution: {integrity: sha512-c14fs5QLLanldcZpX/GjIEKeo++NDzOlixUZ7IUWzN7AoBTisYyWxaxdXNhpAP5I1mPcd92Zagq8sdgTnUXWjg==}
|
||||
dependencies:
|
||||
bun-types: 1.0.26
|
||||
dev: true
|
||||
|
||||
/@types/debug@4.1.12:
|
||||
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
||||
dependencies:
|
||||
|
|
@ -1858,7 +1870,12 @@ packages:
|
|||
resolution: {integrity: sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==}
|
||||
dependencies:
|
||||
undici-types: 5.26.5
|
||||
dev: false
|
||||
|
||||
/@types/node@20.11.17:
|
||||
resolution: {integrity: sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==}
|
||||
dependencies:
|
||||
undici-types: 5.26.5
|
||||
dev: true
|
||||
|
||||
/@types/normalize-package-data@2.4.4:
|
||||
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
|
||||
|
|
@ -1886,6 +1903,12 @@ packages:
|
|||
resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==}
|
||||
dev: false
|
||||
|
||||
/@types/ws@8.5.10:
|
||||
resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
|
||||
dependencies:
|
||||
'@types/node': 20.10.4
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin@6.10.0(@typescript-eslint/parser@6.10.0)(eslint@8.53.0)(typescript@5.3.3):
|
||||
resolution: {integrity: sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==}
|
||||
engines: {node: ^16.0.0 || >=18.0.0}
|
||||
|
|
@ -2646,6 +2669,13 @@ packages:
|
|||
semver: 7.5.4
|
||||
dev: false
|
||||
|
||||
/bun-types@1.0.26:
|
||||
resolution: {integrity: sha512-VcSj+SCaWIcMb0uSGIAtr8P92zq9q+unavcQmx27fk6HulCthXHBVrdGuXxAZbFtv7bHVjizRzR2mk9r/U8Nkg==}
|
||||
dependencies:
|
||||
'@types/node': 20.11.17
|
||||
'@types/ws': 8.5.10
|
||||
dev: true
|
||||
|
||||
/bundle-name@3.0.0:
|
||||
resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==}
|
||||
engines: {node: '>=12'}
|
||||
|
|
@ -8603,7 +8633,6 @@ packages:
|
|||
|
||||
/undici-types@5.26.5:
|
||||
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
|
||||
dev: false
|
||||
|
||||
/unherit@3.0.1:
|
||||
resolution: {integrity: sha512-akOOQ/Yln8a2sgcLj4U0Jmx0R5jpIg2IUyRrWOzmEbjBtGzBdHtSeFKgoEcoH4KYIG/Pb8GQ/BwtYm0GCq1Sqg==}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue