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 Resolve Module with explicit .ts #2055

Closed
jljorgenson18 opened this issue Apr 12, 2019 · 5 comments
Closed

Can't Resolve Module with explicit .ts #2055

jljorgenson18 opened this issue Apr 12, 2019 · 5 comments

Comments

@jljorgenson18
Copy link

jljorgenson18 commented Apr 12, 2019

I'm having issues with wallaby and webpack where certain modules are not resolved. If we add an explicit .ts to the module import path, wallaby can't seem to find it. This seems to have just started. There errors look like

Module not found: Error: Can't resolve '../../utils/mark.ts' in './src/apps/modules/link'ModuleNotFoundError: Module not found: Error: Can't resolve '../../utils/mark.ts' in './src/apps/modules/link' 

The wallaby config looks like

process.env.NODE_ENV = 'wallaby';
const path = require('path');
const wallabyWebpack = require('wallaby-webpack');
const flatten = require('flat');
const set = require('lodash/set');
const isString = require('lodash/isString');
const webpackConfig = require('./webpack.config.js');

const correctWebpackConfigForWallabyCache = (cfg, wallaby) => {
    const contextDir = cfg.context;
    const flattened = flatten(cfg);
    Object.keys(flattened).forEach(key => {
        const val = flattened[key];
        if (
            isString(val) &&
            val.indexOf(contextDir) === 0 &&
            // These should still point to the current directory, not the wallaby cache
            val.indexOf('/node_modules') === -1 &&
            val.indexOf('lib/closure/') === -1
        ) {
            set(cfg, key, val.replace(contextDir, wallaby.projectCacheDir));
        }
    });
    return cfg;
};

module.exports = wallaby => {
    const tests = ['tests/unit/**/*', 'tests/sys/**/*'];
    let wallabyWebpackConfig = {
        ...webpackConfig,
        entryPatterns: ['tests/setup.js', ...tests],
        devtool: 'cheap-module-source-map',
        // This helps speed up the build so we don't add these files to the bundle each time
        externals: {
            // Use external version of React and ReactDOM
            react: 'React',
            'react-dom': 'ReactDOM',
            yjs: 'Y',
            'en-y-websockets-client': 'yWebsocketsClient',
            chai: 'chai',
            sinon: 'sinon'
        }
    };
    delete wallabyWebpackConfig.output;
    const allowedPlugins = {
        ProvidePlugin: true,
        DefinePlugin: true
    };
    // Wallaby will compile this for us
    wallabyWebpackConfig.module.rules = wallabyWebpackConfig.module.rules.filter(l => {
        return l.loader !== 'babel-loader';
    });
    wallabyWebpackConfig.plugins = wallabyWebpackConfig.plugins.filter(
        plugin => allowedPlugins[plugin.constructor.name]
    );
    wallabyWebpackConfig = correctWebpackConfigForWallabyCache(wallabyWebpackConfig, wallaby);
    wallabyWebpackConfig.resolve.alias.tests = path.resolve(wallaby.projectCacheDir, './tests');
    const wallabyPostprocessor = wallabyWebpack(wallabyWebpackConfig);
    return {
        files: [
            // Files we want loaded via script tag for performance reasons
            { pattern: 'node_modules/react/umd/react.production.min.js', instrument: false },
            { pattern: 'node_modules/react-dom/umd/react-dom.production.min.js', instrument: false },
            { pattern: 'node_modules/yjs/y.js', instrument: false },
            { pattern: 'node_modules/en-y-websockets-client/y-websockets-client.js', instrument: false },
            { pattern: 'node_modules/chai/chai.js', instrument: false },
            { pattern: 'node_modules/sinon/pkg/sinon-no-sourcemaps.js', instrument: false },
            // Extra stuff
            { pattern: 'images/**/*', instrument: false, load: false, binary: true },
            { pattern: '*.config.js', instrument: false, load: false },
            { pattern: '.browserslistrc', instrument: false, load: false },
            // Test Files
            { pattern: 'tests/auxiliary/**/*', instrument: true, load: false },
            { pattern: 'tests/resources/**/*', instrument: true, load: false },
            { pattern: 'tests/images/**/*', instrument: true, load: false, binary: true },
            { pattern: 'tests/setup.js', instrument: true, load: false },
            // Primary Code
            { pattern: 'src/**/*', instrument: true, load: false }
        ],

        tests: tests.map(glob => {
            return {
                pattern: glob,
                load: false
            };
        }),
        testFramework: 'mocha',

        // The default is Phantom so use chrome
        env: { kind: 'chrome' },

        postprocessor: wallabyPostprocessor,

        compilers: {
            '**/*.js?(x)': wallaby.compilers.babel(),
            '**/*.ts?(x)': wallaby.compilers.babel()
        },

        debug: false,

        setup() {
            mocha.setup({ timeout: 20000 });
            EN.listenTo('ready', () => window.__moduleBundler.loadTests());
        }
    };
};

The babel config looks like

// https://babeljs.io/docs/en/config-files#project-wide-configuration
module.exports = api => {
    const env = process.env.NODE_ENV;
    api.cache.using(() => env);
    const isTest = env === 'test' || env === 'test-coverage' || env === 'wallaby';
    const isWallaby = env === 'wallaby';
    const isDev = env === 'development';
    return {
        presets: [
            '@babel/preset-typescript',
            [
                '@babel/preset-env',
                // Uses .browserslistrc to find the right targets
                {
                    modules: false,
                    debug: false,
                    useBuiltIns: 'entry',
                    corejs: '3'
                }
            ],
            '@babel/preset-react'
        ],
        plugins: [
            '@babel/plugin-syntax-dynamic-import',
            env === 'test-coverage'
                ? [
                      'babel-plugin-istanbul',
                      {
                          include: ['src/**'],
                          all: true
                      }
                  ]
                : null,
            '@babel/plugin-proposal-class-properties',
            isWallaby ? 'dynamic-import-webpack' : null,
            '@babel/plugin-proposal-object-rest-spread',
            'lodash',
            isDev && !isTest ? 'react-hot-loader/babel' : null
            // Removing rewire for now until it works with babel-preset-typescript
            // https://github.com/speedskater/babel-plugin-rewire/issues/218
            // https://github.com/speedskater/babel-plugin-rewire/pull/219
            // isTest ? 'rewire' : null
        ].filter(Boolean)
    };
};

And we currently have this for our Webpack resolve section

    resolve: {
        extensions: ['.tsx', '.ts', '.js', '.jsx', 'json'],
        alias: {
            images: path.join(__dirname, 'images'),
            src: path.join(__dirname, 'src')
        }
    }
@ArtemGovorov
Copy link
Member

Wallaby for webpack never supported file imports with explicit .ts extension, because Wallaby TS compiler renames .ts files to .js.

You can add a preprocessor to rename compiled files back to .ts, however what is the reason why you are importing files and specifying an explicit file extension? Importing without .ts should work for you (with and without Wallaby).

@jljorgenson18
Copy link
Author

It's a large repo and a few developers like adding explicit extensions to differentiate between directories. I'm the only one using Wallaby at the moment and would rather not clean up after them every time an import with .ts is used. Any idea what the preprocessor would look like?

@ArtemGovorov
Copy link
Member

adding explicit extensions to differentiate between directories.

If I'm not wrong, this approach is only complicating things. Even TypeScript compiler by default is unhappy about imports with file extensions.

Any idea what the preprocessor would look like?

I would highly recommend against it (haven't seen any of our users doing it, so can't guarantee the solidness of the approach), and would rather recommend fixing the code, but if you really can not do, here is the way:

     compilers: {
         '**/*.js?(x)': wallaby.compilers.babel(),
         '**/*.ts?(x)': wallaby.compilers.babel()
     },

+    preprocessors: {
+      '**/*.js': file => require('fs').existsSync(path.join(wallaby.projectCacheDir, file.path.replace('.js', '.ts')))
+          ? file.changeExt('ts').content : file.content
+    },

@jljorgenson18
Copy link
Author

Do you have any references on how adding the extensions causes issues for the Typescript compiler?

@ArtemGovorov
Copy link
Member

I was referring to this default behaviour of Typescript compiler:

Screen Shot 2019-04-14 at 8 47 23 am

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

No branches or pull requests

2 participants