Skip to content

[SvelteKit] Dynamically read hooks file location from svelte.config.js #247

@Lms24

Description

@Lms24

To properly add our SDK to an existing Svelte project with the wizard, we need to put our SDK initialization call into the hooks.(client|server).(js|ts) files. This would be fairly straight forward, if the location of these files wasn't configurable in svelte.config.js:

import { getHooksPath } from 'someModule'
const config = {
  kit: {
    //...
    files: {
      hooks: {
        client: getHooksPath(),
        server: 'src/hooks/server.ts',
      }
    }
  }
};
export default config;

Since these paths are configurable, we need to read the Svelte Config file and use the paths it provides (or fall back to defaults if users didn't specify them). Sounds easy? Sure, just dynamically import the file and we'Redone. Here comes the problem:

svelte.config.js is an ES module but our wizard code is transpiled to CJS. We're using tsc to transpile/downlevel our TS code. By default and in the currently used TS version, every dynamic import (i.e. await import('path/to'svelte'config')) is transpiled to require('path/to'svelte'config). With require, we cannot import ESM, only CJS. This is well documented in this issue. Example: microsoft/TypeScript#43329

Options:

1. Use Rollup 3 to transpile

Instead of tsc, we use rollup 3 to transpile the wizard code. It can stay CJS (it has to, I believe) but Rollup 3 provides the option to keep dynamic import calls in CJS. This will work if Node >= 13 is used (minimum supported node version). We did this in the SvelteKit SDK (where we also read the svelte config) but there it was easy to justify, as Node 16 is oldest supported Node version for SvelteKit. In this case, we'd not be able to support Node 6-12, which we currently do (according to the package.json).

2. Use a "workaround"

We can sort of work around changing our transpilation mechanism by applying a new Function('import("path/to/config")') hack, inspired by this solution. I'm obviously not a fan of using this but considering that the path to the config file doesn't need to be user-configurable, I'm not sure if, besides the ugliness, there is still a security concern. This will still fail in older Node versions but I believe this is fine, given that Node 16 is required for SvelteKit projects anyway.

3. Use a newer typescript/tsc version

Basically identical to 1 but I'd argue that if we make a transpiler change, we might as well switch to Rollup.

Other options considered:

  • Just parsing the config file won't work, as we have to evaluate the file (see example above; the function call to get the client hook directory needs to be evaluated) ==> same problem.
  • Asking the users during the setup to provide a path if they customized the hooks file location seems cumbersome and sort of defeats the purpose of the wizard

We'll need to tackle this in some way. Writing it down because it's yet another ESM/CJS problem we faced and it might have implications for other teams who also rely on the wizard.

Metadata

Metadata

Assignees

Labels

No labels
No labels
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions