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

Can't import workbox in typescript tests #27

Closed
StarpTech opened this issue Apr 18, 2021 · 14 comments · Fixed by #32
Closed

Can't import workbox in typescript tests #27

StarpTech opened this issue Apr 18, 2021 · 14 comments · Fixed by #32

Comments

@StarpTech
Copy link

Hello, I can't import worktop in a test because it loads .mjs files and I have no idea how to load .mjs with typescript files. I use node-tap as test runner. Maybe it's possible to fix it with https://nodejs.org/api/packages.html#packages_conditional_exports

.../node_modules/worktop/request/index.mjs:18
src/routes/foo.test.ts 2> export {
SyntaxError: Unexpected token 'export'

package.json

  "scripts": {
    "test": "tap --ts",
  },
@lukeed
Copy link
Owner

lukeed commented Apr 18, 2021

Hey, nope, will not add conditional exports for this project.
This is a TS limitation that is still being worked through.

For the time being, you can either precompile your TS into JS with ESM format, and then use a test runner that supports ESM (tap does); or you can use a require hook that transforms everything on the fly into ESM or CommonJS, depending on your runtime. For example, something like this would work everywhere: uvu -r esbuild-register tests (configured via tsconfig.json file).

Hope that helps~!

@lukeed lukeed closed this as completed Apr 18, 2021
@StarpTech
Copy link
Author

Still doesn't work same error mesage, any idea?

    "test": "tap --node-arg=\"--require=esbuild-register\"",

@StarpTech
Copy link
Author

with uvu

    "test": "uvu src \".test.ts$\" -r esbuild-register",
.../node_modules/worktop/request/index.mjs:18
src/routes/foo.test.ts 2> export {
SyntaxError: Unexpected token 'export'

@lukeed
Copy link
Owner

lukeed commented Apr 19, 2021

Here's an example setup:

// src/index.ts
import { encode } from 'worktop/utils';

export function howdy(input: string) {
  return encode(input);
}

// test/index.ts
import { test } from 'uvu';
import * as assert from 'uvu/assert';
import * as app from '../src';

test('should be a function', () => {
  assert.type(app.howdy, 'function');
});

test('should be encoded', () => {
  let output = app.howdy('hello');

  assert.is.not(output, 'hello');
  assert.instance(output, Uint8Array);
});

test.run();
// package.json
{
  "scripts": {
    "test": "uvu -r ./bin/register.js test"
  },
  "devDependencies": {
    "esbuild": "0.11.12",
    "uvu": "0.5.1",
    "worktop": "0.4.2"
  }
}
// bin/esbuild.js
const esbuild = require('esbuild');

/** @type {esbuild.CommonOptions} */
const options = {
  target: 'es2019',
  sourcemap: false,
  treeShaking: true,
  minifySyntax: true,
  minifyIdentifiers: true,
}

/**
 * @param {string} input
 * @param {string} output
 * @param {string[]} [externals]
 */
exports.build = function (input, output, externals=[]) {
  return esbuild.build({
    ...options,
    bundle: true,
    format: 'esm',
    outfile: output,
    entryPoints: [input],
    external: externals,
  });
}

/**
 * @param {string} source
 * @param {esbuild.TransformOptions} [overrides]
 */
exports.transform = function (source, overrides={}) {
  return esbuild.transformSync(source, {
    ...options,
    format: 'cjs',
    ...overrides
  });
}

// bin/register.js
const { transform } = require('./esbuild');

const loadCJS = require.extensions['.js'];

/**
 * @param {string} extn
 * @param {string} loader
 */
function loader(extn, loader) {
  require.extensions[extn] = function (Module, filename) {
    const pitch = Module._compile.bind(Module);

    Module._compile = source => {
      const { code, warnings } = transform(source, {
        sourcefile: filename,
        loader: loader,
      });

      warnings.forEach(msg => {
        console.warn(`\nesbuild warning in ${filename}:`);
        console.warn(msg.location);
        console.warn(msg.text);
      });

      return pitch(code, filename);
    };

    loadCJS(Module, filename);
  }
}

loader('.ts', 'ts');
loader('.mjs', 'js');
loader('.cjs', 'js');

Note: The two /bin files are (practically) cloned from worktop's testing setup

But yeah – sorry, forgot that esbuild-register ignores .mjs files by default. I don't know of a way around it – I always just have my own bin/register script.

@StarpTech
Copy link
Author

@lukeed thank you! I'll try it out. Are you sure you won't support cjs in worktop? I'd create a PR 😄

@lukeed
Copy link
Owner

lukeed commented Apr 19, 2021

Yup, 100% sure. Thank you though

@StarpTech
Copy link
Author

Works! We should document it.

@lukeed
Copy link
Owner

lukeed commented Apr 19, 2021

Cool, will do 👍 I'm likely to extract this register script into its own package since I've needed it a number of times.

@StarpTech
Copy link
Author

Do you have type-check when running the tests?

@StarpTech
Copy link
Author

I see you run it explicitly.

@lukeed
Copy link
Owner

lukeed commented Apr 19, 2021

No, in every project I always have it as a separate process. My IDE type checks for live-feedback. I don't need it to run again & again & again for tests – catastrophic errors will fail compilation.

I'll also add a pretest npm-script that runs tsc for me, most of the time.

@StarpTech
Copy link
Author

Make sense.

@StarpTech
Copy link
Author

It looks like that this step breaks debugging entirely. The breakpoints aren't longer mapped to the right code location.

@lukeed
Copy link
Owner

lukeed commented Apr 20, 2021

Yeah, that's the missing part of the bin/*.js scripts. I don't have sourcemap support in here yet but it should be fairly straightforward. TBH that's the missing piece before actually extracting this as a separate module.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants