wallabyjs / public

Repository for Wallaby.js questions and issues
http://wallabyjs.com
760 stars 45 forks source link

​​[Error] Runtime error: TypeError: Cannot read properties of undefined (reading 'meta')​​ #3173

Closed DaveAllbirds closed 1 year ago

DaveAllbirds commented 1 year ago

Issue description or question

Running Shopify's Hydrogen v1 and getting errors on .server files when trying to run Wallaby.

I get the following error:

​[Info]​ 2023-02-26T07:36:07.156Z workers Sandbox (inactive) [pr0lb] error: TypeError: Cannot read properties of undefined (reading 'meta')
​[Info]​     at Context.load (./node_modules/@shopify/hydrogen/vendor/react-server-dom-vite/cjs/react-server-dom-vite-plugin.js:222:25)
​[Info]​     at Object.load (./node_modules/vite/dist/node/chunks/dep-80fe9c6b.js:39268:50)
​[Info]​     at async doTransform (./node_modules/vite/dist/node/chunks/dep-80fe9c6b.js:49951:24)

I've also been running into issues with the wallaby query string appended:

Runtime error: Cannot import /src/utils/logger.server from "./src/api/useContentQuery.test.server.jsx?wallaby=1677357421256". By react-server convention, .server.js files can only be imported from other .server.js files. That way nobody accidentally sends these to the client by indirectly importing it.​​

Wallaby diagnostics report

{
  editorVersion: '1.75.1',
  pluginVersion: '1.0.349',
  editorType: 'VSCode',
  osVersion: 'darwin 21.6.0',
  nodeVersion: 'v16.18.1',
  coreVersion: '1.0.1386',
  checksum: 'YzBhOTE3NmUzNWVhMzM5ZTA5Y2NhNDRhMTM1ZDM1NmMsMTcwODczMjgwMDAwMCww',
  config: {
    trace: true,
    files: [
      { pattern: 'src/**/*.test.js', ignore: true, trigger: true, load: true, file: true },
      { pattern: 'src/**/*.test.jsx', ignore: true, trigger: true, load: true, file: true },
      { pattern: 'src/**/*.test.server.js', ignore: true, trigger: true, load: true, file: true },
      { pattern: 'src/**/*.test.server.jsx', ignore: true, trigger: true, load: true, file: true },
      { pattern: 'src/**/*.test.client.jsx', ignore: true, trigger: true, load: true, file: true },
      { pattern: 'src/**/*.server.test.js', ignore: true, trigger: true, load: true, file: true },
      { pattern: 'src/**/*.server.test.jsx', ignore: true, trigger: true, load: true, file: true },
      { pattern: 'src/**/*.client.test.jsx', ignore: true, trigger: true, load: true, file: true },
      { pattern: 'worker/**/*.test.js', ignore: true, trigger: true, load: true, file: true },
      { pattern: 'worker/**/*.test.server.js', ignore: true, trigger: true, load: true, file: true },
      { pattern: 'worker/**/*.server.test.js', ignore: true, trigger: true, load: true, file: true },
      { pattern: 'src/**/*.js', ignore: false, trigger: true, load: true, order: 1 },
      { pattern: 'src/**/*.jsx', ignore: false, trigger: true, load: true, order: 2 },
      { pattern: 'worker/**/*.js', ignore: false, trigger: true, load: true, order: 3 }
    ],
    tests: [
      { pattern: 'src/**/*.test.js', ignore: false, trigger: true, load: true, test: true, order: 4 },
      { pattern: 'src/**/*.test.jsx', ignore: false, trigger: true, load: true, test: true, order: 5 },
      { pattern: 'src/**/*.test.client.jsx', ignore: false, trigger: true, load: true, test: true, order: 6 },
      { pattern: 'src/**/*.client.test.jsx', ignore: false, trigger: true, load: true, test: true, order: 7 },
      { pattern: 'src/**/*.test.server.jsx', ignore: false, trigger: true, load: true, test: true, order: 8 },
      { pattern: 'src/**/*.test.server.js', ignore: false, trigger: true, load: true, test: true, order: 9 },
      { pattern: 'src/**/*.server.test.js', ignore: false, trigger: true, load: true, test: true, order: 10 },
      { pattern: 'src/**/*.server.test.jsx', ignore: false, trigger: true, load: true, test: true, order: 11 },
      { pattern: 'worker/**/*.test.js', ignore: false, trigger: true, load: true, test: true, order: 12 },
      { pattern: 'worker/**/*.test.server.js', ignore: false, trigger: true, load: true, test: true, order: 13 },
      { pattern: 'worker/**/*.server.test.js', ignore: false, trigger: true, load: true, test: true, order: 14 }
    ],
    diagnostics: {
      vitest: {
        file: {
          config: "import 'dotenv/config';\n" +
            '\n' +
            "import {defineConfig} from 'vite';\n" +
            "import {configDefaults} from 'vitest/config';\n" +
            "import hydrogen from '@shopify/hydrogen/plugin';\n" +
            '\n' +
            '// https://vitejs.dev/config/\n' +
            'export default defineConfig({\n' +
            '  plugins: [\n' +
            '    hydrogen({\n' +
            '      devCache: true,\n' +
            '      purgeQueryCacheOnBuild: false,\n' +
            '    }),\n' +
            '  ],\n' +
            '  // Remove CSS Module Hashes in Vitest env\n' +
            '  css: process.env.VITEST\n' +
            '    ? {\n' +
            '        modules: {\n' +
            "          generateScopedName: '_[local]_',\n" +
            '        },\n' +
            '      }\n' +
            '    : undefined,\n' +
            '  ssr: {\n' +
            "    noExternal: ['@splidejs/react-splide', '@popperjs/core'],\n" +
            '  },\n' +
            '  optimizeDeps: {\n' +
            '    include: [\n' +
            "      '@contentful/rich-text-react-renderer',\n" +
            "      '@contentful/rich-text-types',\n" +
            "      '@datadog/browser-logs',\n" +
            "      '@dhmk/zustand-lens',\n" +
            "      '@headlessui/react',\n" +
            "      '@popperjs/core',\n" +
            "      '@react-hookz/web',\n" +
            "      '@splidejs/react-splide',\n" +
            "      '@turf/turf',\n" +
            "      '@mapbox/mapbox-gl-geocoder',\n" +
            "      'accounting',\n" +
            "      'classnames',\n" +
            "      'dayjs',\n" +
            "      'hash.js',\n" +
            "      'he',\n" +
            "      'immer',\n" +
            "      'js-cookie',\n" +
            "      'lazysizes',\n" +
            "      'lodash',\n" +
            "      'mapbox-gl',\n" +
            "      'prop-types',\n" +
            "      'query-string',\n" +
            "      'react-content-loader',\n" +
            "      'react-map-gl',\n" +
            "      'react-modal',\n" +
            "      'react-player',\n" +
            "      'react-popper',\n" +
            "      'react-transition-group',\n" +
            "      'smoothscroll-polyfill',\n" +
            "      'swiper',\n" +
            "      'swiper/react',\n" +
            "      'trackjs',\n" +
            "      'uuid',\n" +
            "      'zustand',\n" +
            "      'zustand/middleware',\n" +
            "      'zustand/shallow',\n" +
            '    ],\n' +
            '  },\n' +
            '  resolve: {\n' +
            '    alias: {\n' +
            "      api: '/src/api',\n" +
            "      components: '/src/components',\n" +
            "      constants: '/src/constants',\n" +
            "      factories: '/src/factories',\n" +
            "      hooks: '/src/hooks',\n" +
            "      models: '/src/models',\n" +
            "      pages: '/src/pages',\n" +
            "      routes: '/src/routes',\n" +
            "      styles: '/src/styles',\n" +
            "      utils: '/src/utils',\n" +
            "      state: '/src/state',\n" +
            '    },\n' +
            '  },\n' +
            '  test: {\n' +
            '    globals: true,\n' +
            "    environment: 'jsdom',\n" +
            '    testTimeout: 10000,\n' +
            '    hookTimeout: 10000,\n' +
            "    exclude: [...configDefaults.exclude, 'tests/e2e/*'],\n" +
            "    setupFiles: ['./vitest.setup.js'],\n" +
            '  },\n' +
            '});\n'
        },
        config: {
          allowOnly: true,
          watch: true,
          globals: true,
          environment: 'jsdom',
          threads: true,
          clearMocks: false,
          restoreMocks: false,
          mockReset: false,
          include: [ '**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}' ],
          exclude: [ '**/node_modules/**', '**/dist/**', '**/cypress/**', '**/.{idea,git,cache,output,temp}/**', 'tests/e2e/*' ],
          testTimeout: 10000,
          hookTimeout: 10000,
          isolate: true,
          watchExclude: [ '**/node_modules/**', '**/dist/**' ],
          forceRerunTriggers: [],
          update: false,
          reporters: [ 'default' ],
          silent: false,
          ui: false,
          uiBase: '/__vitest__/',
          open: true,
          css: { include: [ {} ] },
          coverage: {
            enabled: false,
            clean: true,
            cleanOnRerun: false,
            reportsDirectory: '<homeDir>/Work/web-app/coverage',
            excludeNodeModules: true,
            exclude: [
              'coverage/**',
              'packages/*/test{,s}/**',
              '**/*.d.ts',
              'cypress/**',
              'test{,s}/**',
              'test{,-*}.{js,cjs,mjs,ts,tsx,jsx}',
              '**/*{.,-}test.{js,cjs,mjs,ts,tsx,jsx}',
              '**/__tests__/**',
              '**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc}.config.{js,cjs,mjs,ts}',
              '**/.{eslint,mocha,prettier}rc.{js,cjs,yml}'
            ],
            reporter: [ 'text', 'html' ],
            allowExternal: false,
            extension: [
              '.js',  '.cjs',
              '.mjs', '.ts',
              '.tsx', '.jsx',
              '.vue', '.svelte'
            ],
            tempDirectory: '<homeDir>/Work/web-app/coverage/tmp'
          },
          fakeTimers: { loopLimit: 10000, shouldClearNativeTimers: true, toFake: [ 'setTimeout', 'clearTimeout', 'setInterval', 'clearInterval', 'setImmediate', 'clearImmediate', 'Date' ] },
          maxConcurrency: 5,
          setupFiles: [ '<homeDir>/Work/web-app/vitest.setup.js' ],
          defines: {},
          root: '<homeDir>/Work/web-app',
          deps: { inline: [ {}, {}, {}, {}, {}, '@nuxt/test-utils', '@splidejs/react-splide', '@popperjs/core', {}, {} ] },
          snapshotOptions: { snapshotFormat: {}, updateSnapshot: 'new' },
          package: {
            version: '0.16.0',
            urls: { hooks: 'file://<homeDir>/.vscode/extensions/wallabyjs.wallaby-vscode-1.0.349/wallaby6da974/runners/node/hooks.mjs' },
            paths: { root: '<homeDir>/Work/web-app/node_modules/vitest', dist: '<homeDir>/Work/web-app/node_modules/vitest/dist' }
          }
        }
      }
    },
    testFramework: { version: 'vitest@0.14.0', configurator: 'vitest@0.14.0', reporter: 'vitest@0.14.0', starter: 'vitest@0.14.0', autoDetected: true },
    preserveComments: false,
    extractComments: true,
    workers: { initial: 1, regular: 1, recycle: false },
    filesWithNoCoverageCalculated: [],
    runAllTestsInAffectedTestFile: false,
    updateNoMoreThanOneSnapshotPerTestFileRun: false,
    compilers: {},
    logLimits: { inline: { depth: 5, elements: 5000 }, values: { default: { stringLength: 200 }, autoExpand: { elements: 5000, stringLength: 8192, depth: 10 } } },
    preprocessors: {},
    maxConsoleMessagesPerTest: 100,
    autoConsoleLog: true,
    delays: { run: 0, edit: 100, update: 0 },
    teardown: undefined,
    hints: {
      ignoreCoverage: '__REGEXP /ignore coverage|istanbul ignore/',
      ignoreCoverageForFile: '__REGEXP /ignore file coverage/',
      commentAutoLog: '?',
      testFileSelection: { include: '__REGEXP /file\\.only/', exclude: '__REGEXP /file\\.skip/' }
    },
    automaticTestFileSelection: true,
    runSelectedTestsOnly: false,
    mapConsoleMessagesStackTrace: false,
    extensions: {},
    env: {
      type: 'node',
      params: { runner: '--experimental-loader=file://<homeDir>/.vscode/extensions/wallabyjs.wallaby-vscode-1.0.349/wallaby6da974/runners/node/hooks.mjs' },
      runner: '<homeDir>/.nvm/versions/node/v16.18.1/bin/node',
      viewportSize: { width: 800, height: 600 },
      options: { width: 800, height: 600 },
      bundle: true
    },
    reportUnhandledPromises: true,
    slowTestThreshold: 75,
    lowCoverageThreshold: 80,
    runAllTestsWhenNoAffectedTests: true,
    symlinkNodeModules: undefined,
    configCode: 'module.exports = {\n' +
      "  autoDetect: ['vitest', 'angular', 'jest'], // default detection order,\n" +
      '  files: [\n' +
      "    {pattern: 'src/**/*.test.*', ignore: true},\n" +
      "    'src/**/*.js',\n" +
      "    'src/**/*.js(x)',\n" +
      "    'worker/**/*.js(x)',\n" +
      '  ],\n' +
      "  tests: ['src/**/*.test.*', 'worker/**/*.test.*'],\n" +
      '};\n'
  },
  packageJSON: {
    dependencies: {
      '@analytics/google-tag-manager': '^0.5.2',
      '@cloudflare/kv-asset-handler': '^0.2.0',
      '@contentful/rich-text-plain-text-renderer': '^15.12.1',
      '@contentful/rich-text-react-renderer': '^15.12.1',
      '@contentful/rich-text-types': '^15.12.1',
      '@datadog/browser-logs': '^4.18.1',
      '@dhmk/zustand-lens': '^2.0.5',
      '@headlessui/react': '^1.6.4',
      '@mapbox/mapbox-gl-geocoder': '^5.0.0',
      '@popperjs/core': '^2.11.6',
      '@react-hookz/web': '^15.0.1',
      '@react-spring/web': '^9.6.1',
      '@shopify/hydrogen': '^1.6.7',
      '@splidejs/react-splide': '^0.7.8',
      '@turf/turf': '^6.5.0',
      accounting: '^0.4.1',
      algoliasearch: '^4.13.1',
      analytics: '^0.8.1',
      'async-retry': '^1.3.3',
      'body-parser': '^1.20.0',
      classnames: '^2.3.1',
      compression: '^1.7.4',
      cookie: '^0.4.2',
      'cross-env': '^7.0.3',
      dayjs: '^1.11.4',
      dotenv: '^16.0.0',
      express: '^4.17.1',
      'hash.js': '^1.1.7',
      he: '^1.2.0',
      immer: '^9.0.15',
      'js-cookie': '^3.0.1',
      lazysizes: '^5.3.2',
      lodash: '^4.17.21',
      'mapbox-gl': '^2.8.0',
      marked: '^4.0.12',
      'path-to-regexp': '^6.2.0',
      'prop-types': '^15.8.1',
      'query-string': '^7.1.1',
      react: '^18.2.0',
      'react-content-loader': '^6.2.0',
      'react-dom': '^18.2.0',
      'react-instantsearch-hooks-web': '^6.26.0',
      'react-map-gl': '^7.0.10',
      'react-modal': '^3.15.1',
      'react-player': '^2.10.1',
      'react-popper': '^2.3.0',
      'react-transition-group': '^4.4.2',
      sass: '^1.50.1',
      'serve-static': '^1.14.1',
      'smoothscroll-polyfill': '^0.4.4',
      swiper: '^8.1.1',
      trackjs: '^3.10.1',
      uuid: '^8.3.2',
      zustand: '^4.0.0'
    },
    devDependencies: {
      '@shopify/cli': '3.0.25',
      '@shopify/cli-hydrogen': '3.0.25',
      '@shopify/prettier-config': '^1.1.2',
      '@tailwindcss/typography': '^0.5.2',
      '@testing-library/jest-dom': '^5.16.5',
      '@testing-library/react': '^13.4.0',
      '@vitest/ui': '^0.7.6',
      eslint: '^8.34.0',
      'eslint-import-resolver-custom-alias': '^1.3.0',
      'eslint-import-resolver-node': '^0.3.7',
      'eslint-plugin-hydrogen': '^0.12.2',
      'eslint-plugin-import': '^2.27.5',
      'eslint-plugin-jest': '^27.2.1',
      jsdom: '^19.0.0',
      'node-fetch': '2',
      'npm-run-all': '^4.1.5',
      playwright: '^1.22.2',
      postcss: '^8.4.14',
      'postcss-import': '^14.1.0',
      'postcss-preset-env': '^7.6.0',
      prettier: '^2.3.2',
      'react-test-renderer': '^18.2.0',
      tailwindcss: '^3.0.24',
      vite: '^2.9.0',
      vitest: '^0.16.0'
    }
  },
  fs: { numberOfFiles: 671 },
  debug: []
}
smcenlly commented 1 year ago

Thanks for reporting the problem. The two errors that you reported are coming from the same file and are caused by the same underlying issue.

The Hydrogen-v1 react-server-dom-vite-plugin is not sanitizing import parameters before attempting to use them (see here). The runtime error is similar in that it's not considering that imports may have a query string parameter (see here).

It is perfectly valid for imports to have a query string appended and Wallaby does this as a part of its cache-breaking mechanism. Vite uses Rollup as it's bundler. The rollup docs actually provide an example of appending a query string suffix, which Wallaby and many other plugins do. Depending on your vite configuration, you would be able to break Hydrogen-v1 in the same way without Wallaby. In Hydrogen's case, the plugin is marked as experimental, (see here) so it may not be fully functional. In contrast, you can see how vitest santizes the request (id parameter) here.

We should be able to get Wallaby working for you by patching Hydrogen-v1's vite plugins so that they correctly process import parameters. Unfortunately we were not able to find a sample or docs for how to set up Hydrogen v1 with vitest (many of the links appear to be broken now, I'm assuming because Hydrogen-v1 is now the legacy version). We need a sample to understand how the files are processed at runtime so that we can create a patch for Hydrogen-v1 and so that we can test it.

--

If you're able to provide us with a sample repo that breaks in the same way, we should be able to fix/patch the relevant Hydrogen libraries, which will allow you to use Wallaby.