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

feat: nuxt3 support #269

Closed
wants to merge 10 commits into from
23 changes: 17 additions & 6 deletions apps/nuxt-e2e/tests/nuxt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,21 @@ describe('nuxt e2e', () => {
await runNxCommandAsyncStripped(`build ${appName}`);
expect(() =>
checkFilesExist(
`dist/apps/${appName}/.nuxt/utils.js`,
`dist/apps/${appName}/.nuxt/client/manifest.json`,
`dist/apps/${appName}/.nuxt/server/client.manifest.json`,
`dist/apps/${appName}/.nuxt/server/client.manifest.mjs`,
`dist/apps/${appName}/.nuxt/server/server.mjs`,
`dist/apps/${appName}/.nuxt/components.d.ts`,
`dist/apps/${appName}/.nuxt/imports.d.ts`,
`dist/apps/${appName}/.nuxt/nuxt.d.ts`,
`dist/apps/${appName}/.nuxt/tsconfig.json`,
`dist/apps/${appName}/.nuxt/.output`,
`dist/apps/${appName}/.nuxt/.output/server`,
`dist/apps/${appName}/.nuxt/.output/nitro.json`,
`dist/apps/${appName}/.nuxt/.output/server/index.mjs`,
`dist/apps/${appName}/.nuxt/views`,
`dist/apps/${appName}/.nuxt/types`
/* `dist/apps/${appName}/.nuxt/utils.js`,
`dist/apps/${appName}/.nuxt/server.js`,
`dist/apps/${appName}/.nuxt/routes.json`,
`dist/apps/${appName}/.nuxt/router.scrollBehavior.js`,
Expand All @@ -48,7 +62,7 @@ describe('nuxt e2e', () => {
`dist/apps/${appName}/.nuxt/vetur`,
`dist/apps/${appName}/.nuxt/mixins`,
`dist/apps/${appName}/.nuxt/dist`,
`dist/apps/${appName}/.nuxt/components`
`dist/apps/${appName}/.nuxt/components` */
)
).not.toThrow();

Expand All @@ -62,10 +76,7 @@ describe('nuxt e2e', () => {
const appName = uniq('app');
await runNxCommandAsyncStripped(`generate @nx-plus/nuxt:app ${appName}`);

updateFile(
`apps/${appName}/pages/index.vue`,
'<script lang="ts">{}</script>'
);
updateFile(`apps/${appName}/app.vue`, '<script lang="ts">{}</script>');

const result = await runNxCommandAsyncStripped(`lint ${appName}`, {
silenceError: true,
Expand Down
1 change: 1 addition & 0 deletions libs/nuxt/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "@nx-plus/nuxt",
"version": "14.1.0",
"main": "src/index.js",
"type": "module",
"generators": "./generators.json",
"executors": "./executors.json",
"description": "Nuxt plugin for Nx",
Expand Down
11 changes: 5 additions & 6 deletions libs/nuxt/src/executors/browser/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ExecutorContext } from '@nrwl/devkit';
import * as path from 'path';

// eslint-disable-next-line @typescript-eslint/no-var-requires
const { build, loadNuxt } = require('nuxt');
import { build, loadNuxt } from 'nuxt';
Copy link
Author

@colinscz colinscz Jul 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ZachJW34 I face an issue with the nx project setup where it's compiling the ESM to CJS. And this results in the error seen on the e2e tests of the pipeline.
You can reproduce it if you

  • run the nuxt lib build and then
  • run the e2e nuxt tests against it.

I have mentioned the two potentially related issues of Nx in the issue description.

I'm open to ideas on how to solve this :)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way I've handled this in the past is to use a little hack utility method:

const dynamicImport = new Function('specifier', 'return import(specifier)');

export default async function* runExecutor(...) {
  const { loadNuxt, build } = await dynamicImport('nuxt') as typeof import('nuxt');
}

That way, the dynamic import won't get converted to a require. Like @Pekes317 pointed out, this can be fixed by changing the "module" target but making this change always leads to more errors in my experience from other dependencies.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ZachJW34 thanks for the hint, I will try out this approach with the latest nuxt framework release and give an update on the status. If that approach is not successful I would give @Pekes317 suggestion a try.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ZachJW34 unfortunately your approach and the one suggested by @Pekes317 still both result in an error.

With both approaches I currently run into a similar error. The latest committed changes are more towards the approach that @Pekes317 suggested. But the tests fail with the following error message, even though the resolutions were changed accordingly:

> nx run nuxt:test

ts-jest[config] (WARN) message TS151001: If you have issues related to imports, you should consider setting `esModuleInterop` to `true` in your TypeScript configuration file (usually `tsconfig.json`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.
 FAIL   nuxt  libs/nuxt/src/generators/application/generator.spec.ts
  ● Test suite failed to run

    libs/nuxt/src/utils.ts:18:17 - error TS1378: Top-level 'await' expressions are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', or 'nodenext', and the 'target' option is set to 'es2017' or higher.

    18 const Module = (await dynamicImport('module')) as typeof import('module');

For now I will revert locally and will add a commit with the suggested approach of @ZachJW34, that way for testing you can switch easily between the different try outs. I will have a go again with your approach @ZachJW34, but if I don't succeed it would be great if you can have a look into it yourself. Thanks in advance :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After going a bit further with the NodeNext approach, I could fix the above issue with some changes to async functions and moving that top level definition to the only function it is used in. I now commited that latest status to remote instead of my previous plan.

It looks like I have now issues with the current version of Jest and the support for ES Modules. You need to run with a version of node that supports ES Modules in the VM API. See https://jestjs.io/docs/ecmascript-modules

This brings me to the following question:

  • Which minimal node version should still be supported with this new plugin version? What is the overall plan for the repository?
  • What about the jest support for ES Modules vs. common-js? Looks like this is still experimental in Node 18.8.0 according to the jest docs: https://jestjs.io/docs/ecmascript-modules --> maybe a switch to vitest right away could mitigate the issue, I haven't checked this yet.

@ZachJW34 You can contact me on Discord if you want to discuss this further and have a quick back and forth on how to tackle this :)
ignotus#3578


export default async function* runExecutor(
options: BrowserExecutorSchema,
Expand All @@ -14,11 +14,10 @@ export default async function* runExecutor(
try {
const projectRoot = await getProjectRoot(context);
const nuxt = await loadNuxt({
for: 'build',
rootDir: projectRoot,
configOverrides: {
config: {
buildDir: path.join(context.root, options.buildDir, '.nuxt'),
build: {
/* build: {
extend(
config: Record<string, unknown>,
ctx: Record<string, unknown>
Expand All @@ -28,14 +27,14 @@ export default async function* runExecutor(
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { default: nuxtConfig } = require(path.join(
projectRoot,
'nuxt.config.js'
'nuxt.config.ts'
));

if (nuxtConfig.build && nuxtConfig.build.extend) {
nuxtConfig.build.extend(config, ctx);
}
},
},
}, */
},
});

Expand Down
14 changes: 7 additions & 7 deletions libs/nuxt/src/executors/server/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ServerExecutorSchema } from './schema';
import * as path from 'path';

// eslint-disable-next-line @typescript-eslint/no-var-requires
const { build, loadNuxt } = require('nuxt');
const { build, loadNuxt } = await import('nuxt');

const serverBuilderOverriddenKeys: string[] = [];

Expand All @@ -31,33 +31,33 @@ export default async function* runExecutor(
const projectRoot = await getProjectRoot(context);

const nuxt = await loadNuxt({
for: options.dev ? 'dev' : 'start',
dev: options.dev,
rootDir: projectRoot,
configOverrides: {
config: {
buildDir: path.join(context.root, browserOptions.buildDir, '.nuxt'),
build: {
/* build: {
extend(config: Record<string, unknown>, ctx: Record<string, unknown>) {
modifyTypescriptAliases(config, projectRoot);

// eslint-disable-next-line @typescript-eslint/no-var-requires
const { default: nuxtConfig } = require(path.join(
projectRoot,
'nuxt.config.js'
'nuxt.config.ts'
));

if (nuxtConfig.build && nuxtConfig.build.extend) {
nuxtConfig.build.extend(config, ctx);
}
},
},
}, */
},
});

if (options.dev) {
await build(nuxt);
}

const result = await nuxt.listen(nuxt.options.server.port);
const result = await nuxt.server();
const baseUrl = options.dev ? nuxt.server.listeners[0].url : result.url;

logger.info(`\nListening on: ${baseUrl}\n`);
Expand Down
21 changes: 11 additions & 10 deletions libs/nuxt/src/executors/static/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import { default as browserExecutor } from '../browser/executor';
import { StaticExecutorSchema } from './schema';

// eslint-disable-next-line @typescript-eslint/no-var-requires
const { Builder, Generator, loadNuxtConfig, Nuxt } = require('nuxt');
import { loadNuxt } from 'nuxt';
// import { Builder, Generator, loadNuxt, Nuxt } from 'nuxt';

const serverBuilderOverriddenKeys: string[] = [];

Expand All @@ -33,15 +34,15 @@ export default async function* runExecutor(

const projectRoot = await getProjectRoot(context);

const config = await loadNuxtConfig({
const config = await loadNuxt({
rootDir: projectRoot,
configOverrides: {
config: {
dev: false,
buildDir: path.join(context.root, browserOptions.buildDir, '.nuxt'),
generate: {
dir: path.join(context.root, browserOptions.buildDir, 'dist'),
},
build: {
/* build: {
extend(
config: Record<string, unknown>,
ctx: Record<string, unknown>
Expand All @@ -58,16 +59,16 @@ export default async function* runExecutor(
nuxtConfig.build.extend(config, ctx);
}
},
},
}, */
},
});

const nuxt = new Nuxt(config);
await nuxt.ready();
const builder = new Builder(nuxt);
const generator = new Generator(nuxt, builder);
//const nuxt = new Nuxt(config);
// await nuxt.ready();
// const builder = new Builder(nuxt);
// const generator = new Generator(nuxt, builder);

await generator.generate({ build: false });
// await generator.generate({ build: false });

yield {
success: true,
Expand Down
5 changes: 5 additions & 0 deletions libs/nuxt/src/generators/application/files/app.vue.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div>
<NuxtWelcome />
</div>
</template>

This file was deleted.

This file was deleted.

43 changes: 0 additions & 43 deletions libs/nuxt/src/generators/application/files/nuxt.config.js.template

This file was deleted.

36 changes: 36 additions & 0 deletions libs/nuxt/src/generators/application/files/nuxt.config.ts.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { defineNuxtConfig } from 'nuxt'

export default defineNuxtConfig({

app: {
// Global page headers: https://v3.nuxtjs.org/api/configuration/nuxt.config#head
head: {
title: 'nuxt-app',
htmlAttrs: {
lang: 'en'
},
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' },
{ name: 'format-detection', content: 'telephone=no' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
},

// Global CSS: https://v3.nuxtjs.org/api/configuration/nuxt.config#css
css: [
],

// Modules for dev and build (recommended): https://v3.nuxtjs.org/api/configuration/nuxt.config#modules
modules: [
],


// Build Configuration: https://v3.nuxtjs.org/api/configuration/nuxt.config#build
build: {
}
})

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@

**This directory is not required, you can delete it if you don't want to use it.**

This directory contains your Vuex Store files.
Vuex Store option is implemented in the Nuxt.js framework.
## Pinia (new official Vue recommendation)
Pinia, which has built-in Nuxt support via a Nuxt module. (https://pinia.vuejs.org/) [Find out more about pinia here].

Creating a file in this directory automatically activates the option in the framework.
This directory contains your Pinia Store files.

More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store).

## Vuex (deprecated)
https://v3.nuxtjs.org/migration/configuration#vuex

If you want to keep using Vuex, you can manually migrate to Vuex 4 following these steps
https://vuex.vuejs.org/guide/migrating-to-4-0-from-3-x.html.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { mount } from '@vue/test-utils'
import App from '../app.vue'

describe('Mounted App', () => {
const wrapper = mount(App);

test('does a wrapper exist', () => {
expect(wrapper.exists()).toBe(true)
})
})

This file was deleted.

Loading