Code Monkey home page Code Monkey logo

quasar-app-extension-ssg's Introduction

Static Site Generator App Extension for Quasar v2, the Vue.js Framework

A Quasar v2 App Extension to generate static site AKA JAMstack.

🆕 Supports Vite since v4.4.0.

npm GitHub code size in bytes GitHub repo size npm Commitizen friendly

This project was created to fill this Feature Request from Quasar.

⚠️ Looking for Quasar v1 with Vue 2 ? See corresponding quasar-app-extension-ssg v2 documentation instead of latest version.

Live Demo | Installing | Uninstalling | Upgrading | Developing | Usage | Configuration | Infos

Live Demo

A live demo built from a fresh new Quasar CLI project (with Vite) is available at https://quasar-app-extension-ssg-vite.netlify.app.

The demo achieves a Google PageSpeed ​​Insights score of 100 for mobile and desktop platforms.

View mobile report

mobile report

View desktop report

desktop report

Installing

Run this command into the Quasar project:

quasar ext add ssg

This will find and install the extension’s module. Once the installation is complete, interactive prompts will wait for responses.

Prompts

Vite
  • add scripts into package.json?: Extends package.json by adding scripts.

    scripts: {
      'build:ssg': 'quasar ssg generate',
      'serve:ssg': 'quasar ssg serve dist/ssg'
    }
  • Add auto-completion of ssg property of quasar.config.js file for IDE ?: Augments configure() helper from quasar/wrappers to get a better IDE autocomplete experience with the extension.

  • Inline critical css and async load the rest ?: Uses Beastcss to inline critical CSS and async load the rest for each generated page.

Webpack
  • add scripts into package.json?: Extends package.json by adding scripts.

    scripts: {
      'build:ssg': 'quasar ssg generate',
      'serve:ssg': 'quasar ssg serve dist/ssg'
    }
  • Add auto-completion of ssg property of quasar.config.js file for IDE ?: Augments configure() helper from quasar/wrappers to get a better IDE autocomplete experience with the extension.

  • Inline critical css and async load the rest ?: Uses Beastcss to inline critical CSS and async load the rest for each generated page.

  • Inline CSS from Vue SFC <style> blocks ?: Inlines css from Vue Single-File Component (SFC) <style> blocks.

Uninstalling

quasar ext remove ssg

Upgrading

This is done with the same command used for installation:

echo y | quasar ext add ssg

Note: It is recommended to overwrite files when requested, so as not to miss any updates.

Usage

Generate

To generate a static site run this command from the quasar project folder:

quasar ssg generate

Generate Options

  • -h, --help: Display usage instructions.
  • --force-build: Force to build the application with webpack.
  • -d, --debug: Build for debugging purposes.

Dev

🆕 Added in v4.2.0

Starts the app in development mode (live reloading, error reporting, etc):

quasar ssg dev

The development server allows to develop the app by compiling and maintaining code in-memory. A web server will serve the app while offering live-reload out of the box. Running in-memory offers faster rebuilds when the code is changed.

The server can be configured by editing the `/quasar.config.js’ file:

devServer: {
  host: '...',
  port: ...
}

Dev Options

  • -h, --help: Display usage instructions.
  • --port, -p: A port number on which to start the application.
  • --hostname, -H: A hostname to use for serving the application.
  • --devtools, -d: Open remote Vue Devtools.

Serve

This extension provides a command to create a server to locally test the generated static site:

quasar ssg serve <dist-folder>

Notes: This server is based on the Quasar cli server adapted for static site. It serves the SPA fallback file (404.html) when a page has not been generated for a given route.

Serve Options

  • --port, -p: Port to use (default: 4000).

  • --hostname, -H: Address to use (default: 0.0.0.0).

  • --prefix-path: Create a virtual path prefix (default: /).

  • --gzip, -g: Compress content (default: true).

  • --silent, -s: Suppress log message.

  • --colors: Log messages with colors (default: true).

  • --open, -o: Open browser window after starting.

  • --cache, -c <number>: Cache time (max-age) in seconds. Does not apply to /service-worker.js (default: 86400 - 24 hours).

  • --micro, -m <seconds>: Use micro-cache (default: 1 second).

  • --https: Enable HTTPS.

  • --cert, -C [path]: Path to SSL cert file (Optional).

  • --key, -K [path]: Path to SSL key file (Optional).

  • --proxy <file.js>: Proxy specific requests defined in file. File must export Array ({ path, rule }). "rule" is defined at: https://github.com/chimurai/http-proxy-middleware.

    module.exports = [
      {
        path: "/api",
        rule: { target: "http://www.example.org" },
      },
    ];
    // will be transformed into app.use(path, httpProxyMiddleware(rule))
  • --cors: Enable CORS for all requests.

  • --help, -h: Display usage instructions.

Inspect

This command can be used to inspect the Webpack config generated by this app extension.

quasar ssg inspect

Inspect Options

Vite
  • --cmd, -c: Quasar SSG command [dev|generate] (default: dev)

  • -d, --depth: Number of levels deep (default: 2).

  • -p, --path: Path of config in dot notation.

    Examples:

    quasar ssg inspect -p build.rollupOptions
    quasar ssg inspect -p plugins
  • --colors: Style output with ANSI color codes (default: true).

  • --thread, -t: Display only one specific ssg config thread

  • -h, --help: Display usage instructions.

Webpack
  • -d, --depth: Number of levels deep (default: 5).

  • -p, --path: Path of config in dot notation.

    Examples:

    quasar ssg inspect -p module.rules
    quasar ssg inspect -p plugins
  • --colors: Style output with ANSI color codes (default: true).

  • -h, --help: Display usage instructions.

Configuration

Options can be passed with ssg key in /quasar.config.js file.

// quasar.config.js

module.exports = function (/* ctx */) {
  return {
    // ...

    ssg: {
      // pass options here
    },

    // ...
  };
};

Webpack

See all available options

concurrency

Type: Number

Default: 10

Page generation is concurrent, ssg.concurrency specifies the amount of page generation that runs in one thread.

interval

Type: Number

Default: 0

Interval in milliseconds between two batches of concurrent page generation to avoid flooding a potential API with calls to the API from the web application.

Notes:

This option is intended to be used in conjunction with the concurrency option. For example, setting concurrency to 10 and interval to 5000 will execute the generation of 10 pages in parallel every 5 seconds.

routes

Type: String[] or Function

Default: []

A list of routes to generate the corresponding pages.

Note: As of quasar-app-extension-ssg v2.0.0 this option is optionnal due to the crawler feature and the ability to include static routes from the app's router using the ssg.includeStaticRoutes option.

If the app has unlinked pages (such as secret pages) and these also need to be generated, the ssg.routes property can be used.

Example:

ssg: {
  routes: ["/", "/about", "/users", "/users/someone"];
}

With a Function which returns a Promise:

// quasar.config.js

const axios = require("axios");

module.exports = function (/* ctx */) {
  return {
    // ...

    ssg: {
      routes() {
        return axios.get("https://my-api/users").then((res) => {
          return res.data.map((user) => {
            return "/users/" + user.id;
          });
        });
      },
    },

    // ...
  };
};

With a Function which returns a callback(err, params):

// quasar.config.js

const axios = require("axios");

module.exports = function (/* ctx */) {
  return {
    // ...

    ssg: {
      routes(callback) {
        axios
          .get("https://my-api/users")
          .then((res) => {
            const routes = res.data.map((user) => {
              return "/users/" + user.id;
            });
            callback(null, routes);
          })
          .catch(callback);
      },
    },

    // ...
  };
};

includeStaticRoutes

🆕 Added in v4.0.0

Type: Boolean

Default: true

Include the application's router static routes to generate the corresponding pages.

Note: In case of warnings issued when initializing routes, this option can be disabled. Then the crawler feature and the ssg.routes options can be used to provide the static and dynamic routes.

distDir

🆕 Added in v4.2.0

Type: String

Default: '<project-folder>/dist/ssg'

Folder where the extension should generate the distributables. Relative path to project root directory.

buildDir

Type: String

Default: '<project-folder>/node_modules/.cache/quasar-app-extension-ssg' or '<project-folder>/.ssg-build' if cache is set to false.

The webpack compilation output folder from where the extension can prerender pages.

cache

Type: Object or false

Default:

{
  ignore: [
    join(conf.ssg.distDir, '/**'), // dist/ssg
    join(conf.ssg.buildDir, '/**'), // node_modules/.cache/quasar-app-extension-ssg
    ...conf.build.distDir ? [join(conf.build.distDir, '/**')] : [],
    'dist/**',
    'public/**',
    'src-ssr/**',
    'src-cordova/**',
    'src-electron/**',
    'src-bex/**',
    'src/ssg.d.ts',
    'node_modules/**',
    '.**/*',
    '.*',
    'README.md'
  ],
  globbyOptions: {
    gitignore: true
  }
}

This option caches the compilation output folder and skips recompilation when no tracked file has changed.

  • ignore is a Globby patterns to ignore tracked files. If an array is provided, it will be merged with default options. A function can be passed to return an array that will remove the defaults.

    Example with an Array:

    ssg: {
      cache: {
        ignore: ["renovate.json"]; // ignore changes applied on this file
      }
    }

    With a Function:

    ssg: {
      cache: {
        ignore: (defaultIgnore) =>
          defaultIgnore.push("renovate.json") && defaultIgnore;
      }
    }
  • globbyOptions can be used to add globby options.

fallback

Type: String

Default: '404.html'

The name of the SPA/PWA fallback file intended to be served when an index.html file does not exist for a given route.

Notes:

  • Overrides build.htmlFilename and build.ssrPwaHtmlFilename.
  • This file is created with the webpack plugin html-webpack-plugin via this Quasar file. It can be extended with some plugins.
  • Multiple services (e.g. Netlify, Vercel) detect a 404.html automatically. For custom web server, an error page must be set up and set to the 404.html file.

crawler

🆕 Added in v2.0.0

Type: Boolean

Default: true

Crawls html links as each page is generated to find dynamic and static routes to add to the page generation queue.

exclude

🆕 Added in v2.0.0

Type: String[] | Regexp[]

An array of routes or regular expressions matching them to prevent corresponding pages from being generated.

Example with an Array of String:

ssg: {
  exclude: ["/my-secret-page"];
}

With an Array of Regexp:

ssg: {
  exclude: [
    /^\/admin/, // path starts with /admin
  ];
}

shouldPreload(file, type, ext, isLazilyHydrated)

🆕 Added in v3.3.0

Type: Function

A function to control what files should have resource hints generated.

By default, no assets will be preloaded.

Example to preload assets:

ssg: {
  shouldPreload: (file, type, ext) => {
    // type is inferred based on the file extension.
    // https://fetch.spec.whatwg.org/#concept-request-destination
    if (type === "script" || type === "style") {
      return true;
    }
    if (type === "font" && ext === "woff2") {
      // only preload woff2 fonts
      return file;
    }
    if (type === "image") {
      // only preload important images
      return file === "hero.jpg";
    }
  };
}

shouldPrefetch(file, type, ext, isLazilyHydrated)

🆕 Added in v3.3.0

Type: Function

A function to control what files should have resource hints generated.

By default no assets will be prefetched. However this is possible to customize what to prefetch in order to better control bandwidth usage. This option expects the same function signature as shouldPreload.

inlineCriticalCss

Type: Boolean or Object

Default: true

Uses Beastcss to inline critical CSS and async load the rest for each generated page.

The default beastcss options can be customized by passing them to inlineCriticalCss.

Example:

ssg: {
  inlineCriticalCss: {
    internal: false,
    merge: false,
  };
}

Notes:

The value is forced to false when using the dev command.

inlineCssFromSFC

🆕 Added in v3.3.0

Type: Boolean

Default: false

Inline css from Vue Single-File Component (SFC) <style> blocks.

Note: This option works even if build.extractCSS is set to true in quasar.config.js file.

Notes:

The value is forced to true when using the dev command.

onRouteRendered(html, route, distDir)

Type: Function

Hook executed after pre-rendering a page just before writing it to the filesystem.

This function must return the html string.

Can use async/await or directly return a Promise.

afterGenerate(files, distDir)

Type: Function

Hook executed after all pages has been generated.

Can use async/await or directly return a Promise.

Note: The files parameter is an Array of all generated page paths + filenames (including the fallback file).

Vite

See all available options

concurrency

Type: Number

Default: 10

Page generation is concurrent, ssg.concurrency specifies the amount of page generation that runs in one thread.

interval

Type: Number

Default: 0

Interval in milliseconds between two batches of concurrent page generation to avoid flooding a potential API with calls to the API from the web application.

Notes:

This option is intended to be used in conjunction with the concurrency option. For example, setting concurrency to 10 and interval to 5000 will execute the generation of 10 pages in parallel every 5 seconds.

routes

Type: String[] or Function

Default: []

A list of routes to generate the corresponding pages.

Note: This option is optionnal due to the crawler feature and the ability to include static routes from the app's router using the ssg.includeStaticRoutes option.

If the app has unlinked pages (such as secret pages) and these also need to be generated, the ssg.routes property can be used.

Example:

ssg: {
  routes: ["/", "/about", "/users", "/users/someone"];
}

With a Function which returns a Promise:

// quasar.config.js

const axios = require("axios");

module.exports = function (/* ctx */) {
  return {
    // ...

    ssg: {
      routes() {
        return axios.get("https://my-api/users").then((res) => {
          return res.data.map((user) => {
            return "/users/" + user.id;
          });
        });
      },
    },

    // ...
  };
};

With a Function which returns a callback(err, params):

// quasar.config.js

const axios = require("axios");

module.exports = function (/* ctx */) {
  return {
    // ...

    ssg: {
      routes(callback) {
        axios
          .get("https://my-api/users")
          .then((res) => {
            const routes = res.data.map((user) => {
              return "/users/" + user.id;
            });
            callback(null, routes);
          })
          .catch(callback);
      },
    },

    // ...
  };
};

includeStaticRoutes

Type: Boolean

Default: true

Include the application's router static routes to generate the corresponding pages.

Note: In case of warnings issued when initializing routes, this option can be disabled. Then the crawler feature and the ssg.routes options can be used to provide the static and dynamic routes.

distDir

Type: String

Default: '<project-folder>/dist/ssg'

Folder where the extension should generate the distributables. Relative path to project root directory.

compilationDir

Type: String

Default: '<project-folder>/node_modules/.cache/quasar-app-extension-ssg' or '<project-folder>/.ssg-compilation' if cache is set to false.

The Vite compilation output folder from where the extension can prerender pages.

cache

Type: Object or false

Default:

{
  ignore: [
    join(conf.ssg.distDir, '/**'), // dist/ssg
    join(conf.ssg.compilationDir, '/**'), // node_modules/.cache/quasar-app-extension-ssg
    join(conf.build.distDir, '/**'),
    'dist/**',
    'src-ssr/**',
    'src-cordova/**',
    'src-electron/**',
    'src-bex/**',
    'src/ssg.d.ts',
    'node_modules/**',
    '.**/*',
    '.*',
    'README.md'
  ],
  globbyOptions: {
    gitignore: true
  }
}

This option caches the compilation output folder and skips recompilation when no tracked file has changed.

  • ignore is a Globby patterns to ignore tracked files. If an array is provided, it will be merged with default options. A function can be passed to return an array that will remove the defaults.

    Example with an Array:

    ssg: {
      cache: {
        ignore: ["renovate.json"]; // ignore changes applied on this file
      }
    }

    With a Function:

    ssg: {
      cache: {
        ignore: (defaultIgnore) =>
          defaultIgnore.push("renovate.json") && defaultIgnore;
      }
    }
  • globbyOptions can be used to add globby options.

fallback

Type: String

Default: '404.html'

The name of the SPA/PWA fallback file intended to be served when an index.html file does not exist for a given route.

Notes:

  • Overrides build.htmlFilename and build.ssrPwaHtmlFilename.
  • This file is created with the webpack plugin html-webpack-plugin via this Quasar file. It can be extended with some plugins.
  • Multiple services (e.g. Netlify, Vercel) detect a 404.html automatically. For custom web server, an error page must be set up and set to the 404.html file.

crawler

Type: Boolean

Default: true

Crawls html links as each page is generated to find dynamic and static routes to add to the page generation queue.

exclude

Type: String[] | Regexp[]

An array of routes or regular expressions matching them to prevent corresponding pages from being generated.

Example with an Array of String:

ssg: {
  exclude: ["/my-secret-page"];
}

With an Array of Regexp:

ssg: {
  exclude: [
    /^\/admin/, // path starts with /admin
  ];
}

shouldPreload({ file, type, extension, isLazilyHydrated })

Type: Function

A function to control what files should have resource hints generated.

By default, no assets will be preloaded.

Example to preload assets:

ssg: {
  shouldPreload: ({ file, type, extension, isLazilyHydrated }) => {
    // type is inferred based on the file extension.
    // https://fetch.spec.whatwg.org/#concept-request-destination
    if (type === "script" || type === "style") {
      return true;
    }

    if (type === "font" && ext === "woff2") {
      // only preload woff2 fonts
      return file;
    }

    if (type === "image") {
      // only preload important images
      return file === "hero.jpg";
    }

    // do not preload anything else
    return false;
  };
}

shouldPrefetch({ file, type, extension, isLazilyHydrated })

Type: Function

A function to control what files should have resource hints generated.

By default no assets will be prefetched. However this is possible to customize what to prefetch in order to better control bandwidth usage. This option expects the same function signature as shouldPreload.

inlineCriticalCss

Type: Boolean or Object

Default: true

Uses Beastcss to inline critical CSS and async load the rest for each generated page.

The default beastcss options can be customized by passing them to inlineCriticalCss.

Example:

ssg: {
  inlineCriticalCss: {
    internal: false,
    merge: false,
  };
}

Notes:

The value is forced to false when using the dev command.

robotoFontDisplay

🆕 Added in v4.5.0

Type: String

Default: 'Optional'

Set the font-display css descriptor of Roboto font imported from @quasar/extras package.

This blog post from the Chrome Developers website can help to choose the best value.

Notes: The Roboto font imported from @quasar/extras package is replaced by its woff2 version (instead of woff) which reduces its weight by half.

With the help of the unicode-range css descriptor the browser will download latin and/or latin-ext variants depending on the characters used in the page.

autoImportSvgIcons

🆕 Added in v4.5.0

Type: Boolean

Default: true

Auto import svg icons from @quasar/extras package.

Notes: For better performance when compiling the application, only icons from the configured Quasar Icon Set are auto imported.

onPageGenerated({ html, route, path })

Type: Function

Hook executed after pre-rendering a page just before writing it to the filesystem. This hook can be used to update html string and/or the generated page output path.

This function must return an Object containing html and path properties.

Can use async/await or directly return a Promise.

Example:

const { join, sep } = require('path');

// skipped code...

ssg: {
  onPageGenerated(page) {
    // do not write generated pages in subfolders

    // replace dist/ssg/some-route/index.html
    // by dist/ssg/some-route.html

    const normalizedRoute = page.route.replace(/\/$/, '');

    const fileName = page.route.length > 1 ? join(sep, normalizedRoute + '.html') : join(sep, 'index.html')

    return {
      html: page.html,
      path: page.path.replace(join(page.route, 'index.html'), fileName),
    };
  },
}

afterGenerate(files, distDir)

Type: Function

Hook executed after all pages has been generated.

Can use async/await or directly return a Promise.

Note: The files parameter is an Array of all generated page paths + filenames (including the fallback file).

Tips

Lazy/partial Hydration

It is possible to lazy hydrate components using the vue3-lazy-hydration package.

In production, when generating pages, the extension does not inject script/preload tags for split chunks corresponding to lazily hydrated components. In this way, these components are loaded client-side, on-demand, when hydration occurs.

Identify running mode

Since the version v4.0.0 the value of process.env.MODE is ssg when the app was built with the command quasar ssg generate or quasar ssg dev.

Below v4.0.0, process.env.STATIC can be used instead.

It could be useful if multiple builds are mixed with different modes to differentiate runtime procedures.

Svg Icons

Svg icons are highly recommended for SSG. The extension tries to reduce the disavantage of using svg by auto importing them.

Example:

// quasar.config.js
module.exports = configure(function (/* ctx */) {
  return {
    // skipped codes...
    framework: {
      iconSet: "svg-material-icons",
    },
  };
});
<!--- default MainLayout.vue --->
<template>
  <q-layout view="lHh Lpr lFf">
    <q-header elevated>
      <q-toolbar>
        <!--- matMenu icon is auto imported -->
        <q-btn
          flat
          dense
          round
          :icon="matMenu"
          aria-label="Menu"
          @click="toggleLeftDrawer"
        />
        <!--- skipped code -->
      </q-toolbar>
    </q-header>
  </q-layout>
</template>

<script>
  // skipped code...

  const linksList = [
    {
      title: "Docs",
      caption: "quasar.dev",
      icon: matSchool, // auto imported icon
      link: "https://quasar.dev",
    },
    // skipped code...
  ];

  export default defineComponent({
    // skipped code....
  });
</script>

Infos

About Boot File

This Extension uses a boot file ssg-corrections.js at client-side to apply corrections to the <body> tag classes.

This is necessary because the running platform is unknown at build time.

About PWA

SSG + PWA can be enabled by setting ssr.pwa to true inside quasar.config.js file.

Vite

Quasar uses workbox-build package to generate a complete service worker and a list of assets to precache which is injected into the service worker file.

This means that all generated pages cannot be precached when Vite is compiling because they do not yet exist at this time. To fix this, when running the generate command, the extension moves the execution of workbox-build methods after all pages have been generated.

Webpack

Quasar uses workbox-webpack-plugin package to generate a complete service worker and a list of assets to precache which is injected into the service worker file.

This means that all generated pages cannot be precached when webpack is compiling because they do not yet exist at this time. To fix this, when running the generate command, the extension instead uses workbox-build package after all pages have been generated.

Therefore, workbox-build options must be passed in the key pwa.workboxOptions of quasar.config.js file instead of the workbox-webpack-plugin options. All other PWA options of the pwa key in the quasar.config.js file are valid and used according to the Quasar documentation.

About Cache Feature

The cache mechanism to avoid recompiling the app when it is not necessary is strongly inspired by Nuxt. See the Nuxt blog post about this feature.

Contributing

The quasar-app-extension-ssg repository is a monorepo using pnpm workspaces. The package manager used to install and link dependencies must be pnpm.

The monorepo contains the extension package inside the packages folder. It also contains private packages in the playground folder for testing the extension with quasar CLI with vite or webpack.

To develop locally, fork the repository then:

  1. Clone it in your local machine.

  2. Run pnpm i in the root folder.

  3. Change the code in the packages/quasar-app-extension-ssg folder.

  4. Run pnpm -w run command:vite or pnpm -w run command:webpack to test the extension commands against private packages in the playground folder.

⚠️ If you intend to directly run Quasar commands in any playground folders, it's advisable to first run in background the pnpm -w run watch command in the root folder.

Testing against external packages

You may wish to test your locally modified copy against an external quasar project that is using the extension. To do this you must specify pnpm.overrides and list the package as a dependency in the root package.json:

{
  "dependencies": {
    "quasar-app-extension-ssg": "^4.7.0"
  },
  "pnpm": {
    "overrides": {
      "quasar-app-extension-ssg": "link:../path/to/forked-repo/packages/quasar-app-extension-ssg"
    }
  }
}

⚠️ If not using pnpm as a package manager it is mandatory to use yalc to avoid possible issues with unresolved dependencies.

quasar-app-extension-ssg's People

Contributors

dependabot[bot] avatar freddy38510 avatar github-actions[bot] avatar lostnet avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

quasar-app-extension-ssg's Issues

Error using InjectManifest pwa mode

Hello, While building using the InjectManifest with pwa:true for ssr, the following error occurs:

Extension(ssg) · ⚠️ ValidationError: "swSrc" is not a supported parameter.

This is my custom manifest:

/*
 * This file (which will be your service worker)
 * is picked up by the build system ONLY if
 * quasar.conf > pwa > workboxPluginMode is set to "InjectManifest"
 */

import * as firebase from "firebase/app";
import "firebase/messaging";

import { setCacheNameDetails, skipWaiting, clientsClaim } from "workbox-core";
import { precacheAndRoute, createHandlerBoundToURL } from "workbox-precaching";
import { registerRoute, NavigationRoute } from "workbox-routing";
import { CacheFirst } from "workbox-strategies";

setCacheNameDetails({ prefix: "quasar-app" });
clientsClaim();
skipWaiting();

self.__precacheManifest = [].concat(self.__WB_MANIFEST || []);
precacheAndRoute(self.__precacheManifest);

registerRoute(
  ({ request }) => request.destination === "image",
  new CacheFirst()
);

registerRoute(
  new NavigationRoute(createHandlerBoundToURL("/offline.html"), {
    denylist: [/service-worker\.js$/, /workbox-(.)*\.js$/]
  })
);
// Initialize the Firebase app in the service worker by passing in
// your app's Firebase config object.
// https://firebase.google.com/docs/web/setup#config-object
firebase.initializeApp({
  
});

// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();

The error doesn't occur when using GenerateSW

Error on build when index.template.html contains an svg tag and some attributes are present

Hi again!
I am using an inline svg in index.template.html. The svg is the following:

<svg
          class="heartbeat-svg"
          viewBox="0 0 219.811 219.811"
          width="234.465"
          height="234.465"
        >
          <clipPath id="a" clipPathUnits="userSpaceOnUse">
            <path
              d="M279.929 559.253s-7.454 8.58-9.434 10.832c-1.98 2.251-7.918 9.528-11.882 17.127-3.965 7.6-8.012 16.869-10.836 25.86-2.824 8.992-3.897 18.642-3.843 28.657.054 10.015.81 20.915 7.338 34.6 6.528 13.685 13.855 24.927 23.764 34.945 9.908 10.019 19.443 19.506 29.706 28.308 10.264 8.803 15.907 14.739 23.415 24.113 7.509 9.375 16.324 22.046 20.27 31.803 3.946 9.757 7.028 16.935 8.736 31.105 1.71 14.17.936 19.816-1.747 32.152-2.683 12.335-6.507 19.748-12.58 31.102s-13.192 21.934-18.874 29.706c7.818-2.796 12.13-4.529 18.522-8.387 6.393-3.859 13.441-8.914 19.572-12.932 6.132-4.018 11.681-9.283 17.124-13.978s9.822-8.729 14.68-15.028c4.856-6.299 10.496-13.009 16.073-24.113s7.736-16.91 8.74-25.511c1.003-8.602.55-22.79-1.748-33.201-2.297-10.412-4.148-14.29-8.387-23.764-4.239-9.474-12.011-21.577-19.223-31.105-7.212-9.529-14.995-17.395-23.764-25.512-8.77-8.116-21.3-18.533-31.454-26.91-10.154-8.376-22.618-17.5-33.198-24.112-10.58-6.613-15.61-10.7-21.32-16.775-5.708-6.077-8.745-15.373-9.785-26.212-1.04-10.84.231-17.68 2.099-27.608 1.868-9.927 4.746-17.385 8.036-25.162zm-27.957 10.832c-12.653 7.725-21.166 12.735-28.657 18.522-7.49 5.787-15.788 10.53-30.404 26.561s-21.548 31.143-25.514 42.287c-3.967 11.143-3.733 26.364 0 44.734 3.732 18.37 8.3 26.021 15.38 35.995 7.078 9.974 23.418 29.89 34.945 40.539 11.526 10.648 20.806 17.775 31.802 27.259 10.996 9.483 24.865 19.19 33.55 25.514 8.685 6.324 11.054 6.842 18.523 13.978 7.468 7.136 13.004 17.11 13.28 30.056.276 12.946-.96 29.792-2.096 44.033 8.012-11.559 16.801-23.373 22.366-36.347 5.564-12.973 8.188-20.261 8.038-39.49-.149-19.227-3.788-29.051-12.23-42.986-8.444-13.935-20.885-26.949-26.213-32.152-5.327-5.203-21.333-17.874-26.212-22.365-4.879-4.492-20.1-20.67-29.006-31.102-8.905-10.432-14.578-21.602-19.568-32.853-4.99-11.25-8.218-24.353-8.04-35.646.18-11.293.717-18.53 3.495-27.607 2.78-9.078 9.946-21.296 12.932-25.864 2.986-4.567 13.63-23.066 13.63-23.066zm40.73 86.232c1.174-.012 2.357.483 3.389 1.502 2.063 2.037 2.039 4.703.042 6.725l-8.311 8.415 12.294 12.142 8.312-8.417c1.996-2.022 4.66-2.08 6.722-.043 2.063 2.037 2.039 4.704.043 6.725l-23.713 24.01c-1.996 2.022-4.66 2.08-6.722.043-2.063-2.038-2.039-4.701-.043-6.723l9.777-9.9-12.294-12.144-9.777 9.902c-1.997 2.021-4.662 2.08-6.725.042-2.063-2.037-2.04-4.7-.043-6.722l23.713-24.013c.998-1.01 2.163-1.531 3.336-1.544zm31.955 32.452c1.297.069 2.522.808 3.719 1.99l15.883 15.686c1.98 1.955 2.853 3.96.734 6.106-2.118 2.146-4.132 1.295-6.112-.661l-13.08-12.914-6.356 6.437 11.47 11.324c1.774 1.752 2.687 3.633.61 5.737-2.078 2.104-3.969 1.215-5.743-.537l-11.47-11.327-7.088 7.18 13.407 13.241c1.98 1.956 2.853 3.958.735 6.103-2.119 2.146-4.132 1.298-6.113-.658l-16.214-16.01c-2.393-2.364-3.022-4.86-.048-7.872l21.51-21.784c1.487-1.505 2.859-2.11 4.156-2.041zm35.179 33.462c1.123.063 2.138.65 3.17 1.668l1.608 1.59c1.526 1.507 2.52 3.223 1.186 6.06l-14.282 30.8c-.404.904-1.172 2.509-1.905 3.251-1.63 1.65-4.376 1.545-6.11-.167-1.485-1.466-2.027-3.062-1.219-4.871l3.155-6.662-10.89-10.756-6.665 3.197c-1.757.872-3.235.472-4.844-1.116-1.857-1.834-1.996-4.252-.203-6.067.733-.743 2.328-1.532 3.227-1.948l31.603-14.509c.782-.358 1.495-.508 2.169-.47zm-3.382 9.704l-15.777 7.972 7.923 7.823 7.935-15.713zm-187.307 2.057c-.944 6.714-1.95 7.69-.7 20.618 1.249 12.928 4.604 25.155 13.28 37.742 8.676 12.588 26.294 34.455 43.336 44.735 17.04 10.28 39.448 27.196 49.624 37.742 10.175 10.545 10.776 14.351 14.33 23.066 3.554 8.715 4.032 17.008 4.54 25.512 2.322-3.416 4.403-12.3 5.595-20.27 1.192-7.97 2.374-11.402.348-24.814-2.025-13.412-20.473-29.689-27.959-34.945-7.486-5.256-18.824-11.51-25.511-16.077-6.687-4.568-29.913-21.074-41.938-32.85-12.024-11.775-22.72-27.774-26.558-34.948-3.838-7.174-8.387-25.511-8.387-25.511zm204.725 3.928c1.363-.121 2.81.544 4.022 1.741l19.557 19.314c1.939 1.915 2.528 4.453.694 6.31-1.833 1.856-4.376 1.299-6.315-.616l-6.398-6.316-20.9 21.165c-1.997 2.021-4.66 2.08-6.722.042-2.063-2.037-2.04-4.703-.043-6.725l20.9-21.164-6.394-6.316c-1.939-1.915-2.531-4.453-.698-6.31.688-.696 1.478-1.052 2.297-1.125z"
            />
          </clipPath>
          <g
            clip-path="url(#a)"
            transform="translate(-66.257 -337.576) scale(.60362)"
            fill="#fff"
          >
            <path
              d="M293.56 923.405c-.509-8.503-.99-16.797-4.543-25.511-3.554-8.715-4.153-12.52-14.329-23.066-10.175-10.545-32.584-27.463-49.625-37.743-17.042-10.28-34.66-32.145-43.335-44.733-8.677-12.588-12.03-24.816-13.28-37.744s-.246-13.904.698-20.619c0 0 4.55 18.338 8.388 25.512 3.838 7.174 14.536 23.172 26.56 34.948 12.024 11.775 35.25 28.282 41.937 32.85 6.687 4.569 18.026 10.82 25.512 16.076 7.486 5.257 25.932 21.536 27.958 34.948 2.026 13.412.843 16.844-.35 24.813-1.191 7.969-3.27 16.854-5.591 20.27z"
            />
            <path
              d="M312.781 919.561c1.136-14.24 2.373-31.087 2.097-44.034-.276-12.946-5.812-22.919-13.28-30.055-7.469-7.136-9.837-7.655-18.522-13.979-8.685-6.324-22.554-16.028-33.55-25.512-10.996-9.483-20.276-16.61-31.802-27.259-11.527-10.648-27.87-30.565-34.948-40.539-7.079-9.974-11.644-17.626-15.377-35.996-3.733-18.37-3.966-33.59 0-44.733 3.966-11.143 10.896-26.256 25.512-42.287 14.616-16.03 22.914-20.773 30.404-26.56 7.49-5.787 16.004-10.797 28.657-18.522 0 0-10.643 18.498-13.63 23.065-2.986 4.568-10.151 16.784-12.93 25.862s-3.316 16.315-3.495 27.608c-.178 11.293 3.048 24.397 8.038 35.647s10.665 22.419 19.571 32.85c8.906 10.433 24.127 26.612 29.006 31.104 4.88 4.492 20.884 17.163 26.211 22.367 5.328 5.203 17.768 18.216 26.21 32.151 8.444 13.935 12.083 23.758 12.233 42.986.149 19.228-2.474 26.517-8.038 39.49-5.565 12.974-14.355 24.787-22.367 36.346z"
            />
            <path
              d="M323.964 919.561c5.683-7.772 12.799-18.351 18.872-29.705s9.898-18.768 12.581-31.104c2.683-12.335 3.457-17.981 1.748-32.151-1.71-14.17-4.79-21.346-8.737-31.104-3.947-9.757-12.762-22.428-20.27-31.802-7.508-9.375-13.151-15.311-23.415-24.114-10.263-8.803-19.797-18.29-29.705-28.308-9.909-10.018-17.237-21.262-23.765-34.947-6.528-13.685-7.285-24.583-7.339-34.598-.054-10.015 1.02-19.666 3.845-28.657 2.824-8.992 6.869-18.262 10.833-25.862 3.965-7.599 9.902-14.873 11.883-17.124 1.98-2.251 9.435-10.834 9.435-10.834-3.289 7.778-6.17 15.235-8.038 25.163-1.867 9.927-3.137 16.769-2.096 27.608 1.04 10.84 4.076 20.135 9.785 26.21 5.709 6.077 10.738 10.164 21.318 16.776 10.58 6.612 23.047 15.737 33.2 24.114 10.154 8.376 22.684 18.793 31.453 26.91 8.77 8.116 16.553 15.983 23.764 25.511 7.212 9.528 14.983 21.63 19.222 31.103 4.238 9.474 6.09 13.353 8.387 23.765 2.297 10.412 2.75 24.598 1.747 33.2-1.003 8.602-3.16 14.408-8.736 25.512-5.577 11.104-11.22 17.815-16.076 24.114-4.857 6.299-9.235 10.332-14.678 15.027-5.443 4.695-10.993 9.961-17.125 13.98-6.131 4.017-13.177 9.071-19.57 12.93-6.393 3.858-10.705 5.591-18.523 8.387z"
            />
          </g>
        </svg>

When I do that I get the following error on build:

Could not inline critical css

Cannot read properties of null (reading '1')

I know that height and width attributes cause the error and also all the attributes in the g tag.
Setting inlineCriticalCss to false doesn't alleviate the error.

There are other divs present in index.template.html but they cause no error.

Thanks in advance.

Can't build with both modes: SSG and SSR.

@freddy38510
I'm trying to split my project and i need it to run half in SSG and the other in SSR.
With that in mind my problem comes when i try to split routes and when i'm asking for the MODE i can't search for ssg.
Also the portion of code that should run in ssr both runs in ssr and ssg.

Any clues on how to separate things?

(also i'm starting to get familiar with routing and dynamic routes) #22

require() of ES modules is not supported (Quasar v2)

================== GENERATE ==================

App • Cleaned build artifact: "C:\apps\vue\h5-off-site\dist\ssg"
Extension(ssg) • Copying assets...
Extension(ssg) • WAIT • Generating pages in progress...
[Vue Router warn]: uncaught error during route navigation:
[ Error: C:\apps\vue\h5-off-site\node_modules\swiper\vue\swiper-vue.js
require() of ES modules is not supported.
require() of C:\apps\vue\h5-off-site\node_modules\swiper\vue\swiper-vue.js from C: \apps\vue\h5-off-site\node_modules.cache\quasar-app-extension-ssg is an ES module file as it is a .js file whose nearest parent package.json contains "type": "modu le" which defines all .js files in that package scope as ES modules.
Instead rename swiper-vue.js to end in .cjs, change the requiring code to use impo rt(), or remove "type": "module" from C:\apps\vue\h5-off-site\node_modules\swiper\ package.json.

  • errors.js:322 new NodeError
    internal/errors.js:322:7

  • loader.js:1102 Object.Module._extensions..js
    internal/modules/cjs/loader.js:1102:13

  • loader.js:950 Module.load
    internal/modules/cjs/loader.js:950:32

  • loader.js:790 Function.Module._load
    internal/modules/cjs/loader.js:790:12

  • loader.js:974 Module.require
    internal/modules/cjs/loader.js:974:19

  • helpers.js:93 require
    internal/modules/cjs/helpers.js:93:18

  • create-bundle.js:56 _require
    [h5-off-site]/[@quasar]/ssr-helpers/lib/create-bundle.js:56:9

  • render-app.js:491 Object.5804
    render-app.js:491:18

  • render-app.js:555 webpack_require
    render-app.js:555:41

  • chunk-392.js:386 Object.8072
    chunk-392.js:386:68

] {
code: 'ERR_REQUIRE_ESM'
}
Extension(ssg) • DONE • Pages generated with errors • 101ms

Extension(ssg) • ERROR • Generate route /

Error: C:\apps\vue\h5-off-site\node_modules\swiper\vue\swiper-vue.js
require() of ES modules is not supported.
require() of C:\apps\vue\h5-off-site\node_modules\swiper\vue\swiper-vue.js from C: \apps\vue\h5-off-site\node_modules.cache\quasar-app-extension-ssg is an ES module file as it is a .js file whose nearest parent package.json contains "type": "modu le" which defines all .js files in that package scope as ES modules.
Instead rename swiper-vue.js to end in .cjs, change the requiring code to use impo rt(), or remove "type": "module" from C:\apps\vue\h5-off-site\node_modules\swiper\ package.json.

Rename extension name to `quasar-app-extension-ssg`

Same reason behind #7, static is a very generic word too.
quasar ext add static is a bit generic, quasar ext add ssg is already clearer

The AE name is expecially important because once a package is added to NPM I think it's impossible to remove it, you can just deprecate it, and littering the registry with duplicates would cause confusion in the ecosystem

Action Required: Fix Renovate Configuration

There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.

Error type: Cannot find preset's package (github>whitesource/merge-confidence:beta)

InjectManifest doesn't build the custom-service-worker

Hello again,
Using InjectManifest causes the custom-service-worker file to be copied as is in the distribution folder without building it.
It causes error on run-time when using imports in the custom-service-worker.js file since webpack isn't used to create the file.

I don't know if the behaviour was intended so I thought it might be better to open an issue about that.

Best regards

Output build into `/build/ssg`

For the same reason behind #3, the output should be put into an ad-hoc folder for SSG.
It could be an idea to also allow specifying the output folder, but I don't actually have an use case in mind for this, once the build folder is already separated from other modes.

Compatibility with @quasar/app 2.1.0

Seems like something breaks when using the AE with @quasar/app v2.1.0

image

I see you already tried to address this in afd9e25 but seems like something isn't working anyway.

Glancing over the code, the fix you proposed seems right, I'm gonna try to dig deeper when I find some time and stick to @quasar/app v2.0.8 until then

Could not pre-render route; Must use import to load ES Module

Hi,
I got one error when trying to generate my site:

Could not pre-render route

Must use import to load ES Module: C:\...\node_modules\three\examples\jsm\controls\OrbitControls.js require() of ES modules is not supported. 
require() of C:\...\node_modules\three\examples\jsm\controls\OrbitControls.js from C:\...\node_modules\.cache\quasar-app-extension-ssg is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename OrbitControls.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from C:\...\node_modules\three\examples\jsm\package.json.

It's a file from ThreeJS working well in SPA, but I don't know what is going on when trying SSG. I don't use require()

Thanks in advance for you help!

Not working with other app extension

I created a project where i used an app extension called @dreamonkey/animate that you can find here https://github.com/dreamonkey/quasar-app-extension-animate.git.
Later when applying SSG AE this error will show up:

  SyntaxError: Cannot use import statement outside a module
...
 Extension(ssg) · ⚠️  SyntaxError: Cannot use import statement outside a module

I think the problem is relative to webpack that can't transpile the dependency of the other app extension.

repro

https://github.com/vandelpavel/app-esxtension-test.git

You can try to launch quasar dev and scrolling the page will activate the animations.
But when you try to launch quasar ssg build it will throw that error.

Create custom SSG commands

Instead of piggybacking on SSR build step, we should create some standalone commands.
Currently it's not possible to enhance existing commands AFAIK (so no quasar [dev/build] -ssg), but we can go with something which look similar and change it later if it becomes possible.

I was thinking about quasar dev-ssg and quasar build-ssg

Failed to install quasar-app-extension-ssg

I try to install this extension and get the next:

D:\Projects\quasor\qsanche>quasar ext add ssg
App · Installing "ssg" Quasar App Extension

App · Retrieving "quasar-app-extension-ssg"...
App · [sync] Running "npm install --save-dev quasar-app-extension-ssg"

npm ERR! code ENOENT
npm ERR! syscall spawn git
npm ERR! path git
npm ERR! errno ENOENT
npm ERR! enoent Error while executing:
npm ERR! enoent undefined ls-remote -h -t https://github.com/freddy38510/critters.git
npm ERR! enoent
npm ERR! enoent
npm ERR! enoent spawn git ENOENT
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent

App · ⚠️ Command "npm" failed with exit code: 1
App · ⚠️ Failed to install quasar-app-extension-ssg

Allow dev mode

I can understand why you enabled it only in production mode, but production build usually execute a lot of time-consuming optimization to the code, which slows down the compilation.
Production build also run a single time, but during development it will be useful to have the code in watch mode while fixing SSG-related bugs.

I think the idea should be to make this AE similar to a Quasar mode (which in part already is), allowing it to be called with something like quasar dev -m ssg and quasar build -m ssg

At some point, after we proved it to work correctly, we could try to PR it as a new Quasar official mode (if Razvan likes the idea).
Especially if a lot of SSR code can be reused for this scenario.

Or, the other way around, this extension could be an useful trial to see how a mode could be detached from Quasar core, consequently allowing to follow the same path and extract other modes as well (and thus making it easier to manage typings for everyone and dropping current Feature Flags system)

Not compatible with @quasar/app v2.0.1

App · ⚠️ Extension(ssg): is not compatible with @quasar/app v2.0.1. Required version: ^1.5.2

I get this error whenever I run quasar ext invoke ssg

? Enable SPA Fallback: Yes
? Set the path to the SPA fallback: 404.html
? Enable CriticalCss: Yes
? Preload strategy: default

 App · Updating /quasar.extensions.json for "ssg" extension ...
 App · Running App Extension install script...
 App · ⚠️  Extension(ssg): is not compatible with @quasar/app v2.0.1. Required version: ^1.5.2```

Getting in contact

Hi Freddy!
Was wondering if there's a way I can get in contact with you?

We're going to mention quasar-app-extension-ssg in the upcoming "State of Vue" conference and was wondering if there's anything you want me to mention?

Sorry to do this in an issue... Did know any other way!

Feel free to email me, or just post a reply here!

[email protected]

Implement uninstall

I guess it's just a matter of removing src-static and src/boot/bodyClasses.js files, given that the webpack chain changes are all into index.js

There're no generated routes in the dist/ssg directory

I am trying to generate some routes, so I added the following in the quasar.conf.js

ssg: { routes: ['/ar', '/ar/explore/10', '/ar/explore/11'] },

and used this command to generate the above routes: npm run build:ssg

The build was completed but with an error: Extension(ssg) · ⚠️ TypeError: d.Cookies.has is not a function

image

Note: I am using Cookies helper from Quasar.

And I checked the dist/ssg folder, and found that the plugin has generated the needed static files (CSS, JS, Images), but it didn't generate the HTML pages

image

Data might be helpful:

"quasar": "^1.11.3"
"@quasar/app": "^1.8.10"
"@quasar/extras": "^1.8.1"

Prompt options: add scripts

Many AE use Prompt API to ask the dev for some initialization options and take care of scaffolding related code.

I think it could be a good idea to add a "add scripts into your package.json" which automatically adds build and serve commands like so:

{
  "scripts": {
    "build:ssg": "quasar ssg generate",
    "serve:ssg": "quasar ssg serve dist/ssg/public"
  }
}

Could not find module "browsers-support" while building

I am trying to generate Static Pages using this extension by this command : build:ssg': 'quasar ssg generate but it fails with following error Error: Cannot find module 'C:\Users\user\repos\abwaabv2\node_modules\@quasar\app\lib\helpers\browsers-support'

Here's a screenshot of the issue:
image

(node:18516) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict(see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2) (node:18516) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

I am not sure, how can I get this "browsers-supports"?

Any insights please.

@vue/preload-webpack-plugin seems not working when generating static site

Hello, i use @vue/preload-webpack-plugin inside quasar.conf.js

    const PreloadWebpackPlugin = require('@vue/preload-webpack-plugin');

    chainWebpack (chain) {

        chain.plugin('preload')
          .use(PreloadWebpackPlugin, [{
            rel: 'preload',
            include: 'initial',
            fileBlacklist: [
              /\.map$/,
              /\.js$/
            ]
        }])
      },

Dev server preloads fonts and styles perfectly,
Screen Shot 2565-02-13 at 14 26 57

but when i build SSG, head links not preloaded.
Screen Shot 2565-02-13 at 14 27 27

Because of this, fonts on the page loads very long time and flashing. Looks ugly.

Is it possible to make preload plugin works with ssg?

Not compatible with TypeScript

Hi, i'v tryed to use this app extension and when i launch the "quasar ssg build" this will show up:
⚠️ TypeError: Cannot assign to read only property 'exports' of object '#<Object>'

I don't know where to find the error inside the project.
Thanks.

Sharing routes paths between router/routes.js and ssg > routes option

I'll drop here a suggestion for an use case, for whom could need it.
I explored a bit how to keep in sync routes path for SSG configuration and router option object.
This is a bit tricky because quasar.conf.js is handled as CommonJS, while router/routes.js is ES5 syntax and the two cannot be mixed, at least until Quasar CLI files will be transpiled too or Node will finally understand ES modules.

The best I could think of was to store the paths as a string dictionary into quasar.conf.js, provide it as an env variable and then retrive and use it into router/routes.js.

// quasar.conf.js
const routesPaths = {
  root: '',
  home: 'home',
  archive: 'archive',
  bookDetail: 'book-detail'
};

const routes = Object.values(routesPaths).map(route => {
  return `/${route}`;
});

module.exports = configure(function(ctx) {
  return {
    ssg: { routes },
    build: {
      env: {
        ROUTES_PATHS: routesPaths
      },
    },
    // ... other options
  };
}
// router/routes.js
/** @type {Object.<string, string>}  */
const routesPaths = process.env.ROUTES_PATHS;

const routes = [
  {
    path: '/',
    component: () => import('layouts/layout.vue'),
    children: [
      {
        path: routesPaths.root,
        component: () => import('pages/home.vue')
      },
      {
        path: routesPaths.home,
        component: () => import('pages/home.vue')
      },
      {
        path: routesPaths.archive,
        component: () => import('pages/archive.vue'),
      },
      {
        path: routesPaths.bookDetail,
        component: () => import('pages/book-detail.vue'),
      }
    ],
  },
];

// Always leave this as last one
routes.push({
  path: '*',
  component: () => import('pages/error-404.vue'),
});

export default routes;

Unluckily I could not find a way to do the opposite yet (automatically build SSG routes from VueRouter configuration), which would be much more useful.
Nuxt does this by crawling the website, which may be an overkill right now.

[Question] Is it possible to build SPA with only specified routes as static webpages?

is it possible to tell the plugin to only generate static code for one specific route or component?

I have a product webpage which updates its product-data only on rare occasions and I would like to generate a static side by pulling the product information from a database and then pre-rendering it duing build stage. but I would only like to render the specific product webpages (only 3) into static code. Is this possible?

Thx for the great addon otherwise! I hope it gets included into quasar main build at some point...

Build succeedes but process hangs forever, making it impossible to deploy with netlify

Dear Freddy/Quasar team

First of all thank you for your amazing work, this plugin is a life saver. There is only 1 issue which prevents me from using it in production.

When I start a build process (quasar ssg generate) it builds perfectly fine, but the process seem to hang forever, which makes it so that Netlify can not move into the 2nd phase of deploying the generated files to their CDN. Is there a way to maybe manually hang the process in the after generation hook? Or is this a bug that you might be able to help me with?

Here is my package.json
"dependencies": {
"@quasar/extras": "^1.0.0",
"awesome-phonenumber": "^2.64.0",
"babel-loader": "^8.2.3",
"clipboard": "^2.0.8",
"core-js": "^3.6.5",
"isomorphic-ws": "^4.0.1",
"quasar": "^2.0.0",
"simpleddp": "^2.2.4",
"simpleddp-plugin-login": "^4.0.2",
"vue-lite-youtube-embed": "^1.0.1",
"vue-recaptcha": "^2.0.1",
"vue3-carousel": "^0.1.30",
"vue3-json-viewer": "^1.0.4"
},
"devDependencies": {
"@babel/eslint-parser": "^7.13.14",
"@quasar/app": "^3.0.0",
"eslint": "^7.14.0",
"eslint-config-standard": "^16.0.2",
"eslint-plugin-import": "^2.19.1",
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-vue": "^7.0.0",
"eslint-webpack-plugin": "^2.4.0",
"netlify-cli": "^6.14.19",
"pug": "^3.0.2",
"pug-plain-loader": "^1.1.0",
"quasar-app-extension-ssg": "^3.1.1"
},

here is the end of the build process log

9:41:13 AM: Extension(ssg) • ❯ Beastcss[info]: Processed in 91 ms
9:41:13 AM: Extension(ssg) • DONE • Pages generated with success • 148229ms
9:41:13 AM: App • Added build artifact "/opt/build/repo/dist/ssg"
9:41:13 AM: Generate succeeded
9:41:13 AM: Pkg quasar........ v2.3.4
9:41:13 AM: Pkg @quasar/app... v3.2.5
9:41:13 AM: Debugging......... enabled
9:41:13 AM: SPA fallback...... 404.html
9:41:13 AM: ==================
9:41:13 AM: Output folder..... /opt/build/repo/dist/ssg
9:41:13 AM: Tip: You can use "$ quasar ssg serve dist/ssg" command to create
9:41:13 AM: a static web server for testing. Type "$ quasar ssg serve -h" for parameters.

And its stuck like this forever.

The same thing happens on my pc in linux.

Here is the log for "quasar info" for my pc, but since the same happens on netlify I believe it is not an environment issue.

[exworm@exworm-pc puregoldprotein-frontend]$ quasar info

Operating System - Linux(5.15.6-arch2-1-surface) - linux/x64
NodeJs - 14.18.1

Global packages
NPM - 6.14.15
yarn - 1.22.17
@quasar/cli - 1.2.2
@quasar/icongenie - 2.4.2
cordova - Not installed

Important local packages
quasar - 2.3.4 -- Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time
@quasar/app - 3.2.5 -- Quasar Framework local CLI
@quasar/extras - 1.12.2 -- Quasar Framework fonts, icons and animations
eslint-plugin-quasar - Not installed
vue - 3.2.24 -- The progressive JavaScript framework for buiding modern web UI.
vue-router - 4.0.12
vuex - Not installed
electron - Not installed
electron-packager - Not installed
electron-builder - Not installed
@babel/core - 7.16.0 -- Babel compiler core.
webpack - 5.65.0 -- Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.
webpack-dev-server - 4.6.0 -- Serves a webpack app. Updates the browser on changes.
workbox-webpack-plugin - Not installed
register-service-worker - 1.7.2 -- Script for registering service worker, with hooks
typescript - 4.4.2 -- TypeScript is a language for application scale JavaScript development
@capacitor/core - Not installed
@capacitor/cli - Not installed
@capacitor/android - Not installed
@capacitor/ios - Not installed

Quasar App Extensions
quasar-app-extension-ssg - 3.1.1 -- Static Site Generator App Extension for Quasar.

Failed to install quasar-app-extension-ssg

quasar ext add ssg
App · Installing "ssg" Quasar App Extension

App · Retrieving "quasar-app-extension-ssg"...
App · [sync] Running "npm install --save-dev quasar-app-extension-ssg"

npm ERR! path git
npm ERR! code ENOENT
npm ERR! errno ENOENT
npm ERR! syscall spawn git
npm ERR! enoent Error while executing:
npm ERR! enoent undefined ls-remote -h -t https://github.com/freddy38510/critters.git
npm ERR! enoent
npm ERR! enoent
npm ERR! enoent spawn git ENOENT
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent

npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\hamana.keigo\AppData\Roaming\npm-cache_logs\2020-12-30T01_48_54_977Z-debug.log

App · ⚠️ Command "npm" failed with exit code: 1
App · ⚠️ Failed to install quasar-app-extension-ssg

Rename folder to `src-ssg`

To be consistent with all other acronym-like modes, should be good to change the folder name to src-ssg instead of src-static

Serve command: Error 404 on Windows

Inside a project i have different pages and a layout defined as in routes.js:

const routes = [
  {
    path: '/',
    component: () => import('layouts/MainLayout.vue'),
    children: [
      { path: '', component: () => import('pages/Test.vue') },
      { path: 'Home', component: () => import('pages/Test.vue') },
],
  },
  { path: '*', component: () => import('pages/error-404.vue') },
];

export default routes;

So i modified the quasar.conf.js with vueRouterMode: 'history'.

Then i added the routes in src-ssg.routes.js:

module.exports = function() {
  return ['/', '/home'];
};

Then i launch quasar ssg build and then quasar serve dist/ssr/www/.

No error yet but when reaching localhost:4000 the console's output is:
GET / [127.0.0.1] Mon Aug 17 2020 18:20:28 GMT+0200 (GMT+02:00) 404 on /

Even when changing routes and typing localhost:4000/home the console will show 404 on /home.

Any ideas?

Package subpath './lib/crc32' is not defined by "exports"

Just created a new project and created and forked your project.
Then inside your project i'v launched:

yarn link
yarn

Then in my project:

yarn link quasar-app-extension-ssg
quasar ext invoke ssg
yarn build:ssg

Then i get this error:

yarn run v1.22.10
$ quasar ssg generate
 App • Looking for Quasar App Extension "ssg" command "generate"
 App • Running "ssg" Quasar App Extension...
 App • Running "ssg" > "generate" command

(node:94981) UnhandledPromiseRejectionWarning:   Error: Package subpath './lib/crc32' is not defined by "exports" in /media/avandelli/DataShared_NTFS/projects/quasar-app-extension-ssg/node_modules/crc/package.json
  
  - resolve.js:285 throwExportsNotFound
    internal/modules/esm/resolve.js:285:9
  
  - resolve.js:508 packageExportsResolve
    internal/modules/esm/resolve.js:508:3
  
  - loader.js:424 resolveExports
    internal/modules/cjs/loader.js:424:36
  
  - loader.js:464 Function.Module._findPath
    internal/modules/cjs/loader.js:464:31
  
  - loader.js:802 Function.Module._resolveFilename
    internal/modules/cjs/loader.js:802:27
  
  - loader.js:667 Function.Module._load
    internal/modules/cjs/loader.js:667:27
  
  - loader.js:887 Module.require
    internal/modules/cjs/loader.js:887:19
  
  - helpers.js:74 require
    internal/modules/cjs/helpers.js:74:18
  
  - snapshot.js:1 Object.<anonymous>
  -
    /media/avandelli/DataShared_NTFS/projects/quasar-app-extension-ssg/src/build/snapshot.js:1:15
  
  - loader.js:999 Module._compile
    internal/modules/cjs/loader.js:999:30
  

(node:94981) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:94981) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Done in 0.44s.

While adding the this AE using quasar ext add ssg works.

Wrong quasar conf file path on latest Quasar

This plugin targets a specific path for the quasar conf file:

const QuasarConfFile = appRequire('@quasar/app/lib/quasar-conf-file', api.appDir);

And after updating Quasar to latest and using the webpack flavour the path that would work is:
@quasar/app-webpack/lib/quasar-conf-file.

I guess then that the plugin needs to consider multiple options here, right now should be either app-webpack or app-vite.

Prompt option: setup critical CSS inlining

Premises are the same as #21

Adding an option for critical CSS inlining is an easily automatable task, an AE prompt to the dev will be a better push to this best-practice instead of asking him to copy/paste the same code from the README

generate files need javascript to render it

Maybe this expected, but i tougth a SSG should generate the html files with the rendered html inside it so the requester would be able to see the page as intended without the need of javascript, the reason for something so complicated like this, would be bot and crawlers for seo to work as it would in SSR

Is this something in the roadmap or even available and i did not found it?
Thanks for the library.

Is it neccesary to use preFetch?

Hi Freddy,

Thank you for this extension, i am trying to implement it but I was wondering if its necessary to use preFetch to be able to generate the pages with all the dynamic data. If I don't and make an api call in setup() the source code is empty.

Regards,
Franco.

http-proxy-middleware TS conflict error

Seems like there is a TS conflict generated by different version of http-proxy-middleware.
See DefinitelyTyped/DefinitelyTyped#45479

ERROR in /home/---/node_modules/@types/webpack-dev-server/index.d.ts(30,53):
TS2694: Namespace '"/home/---/node_modules/http-proxy-middleware/dist/index"' has no exported member 'Config'.
ERROR in /home/---/node_modules/@types/webpack-dev-server/index.d.ts(36,29):
TS2694: Namespace '"/home/---/node_modules/http-proxy-middleware/dist/index"' has no exported member 'Config'.
Version: typescript 3.9.5, eslint 6.8.0
yarn why v1.22.5
[1/4] Why do we have the module "http-proxy-middleware"...?
[2/4] Initialising dependency graph...
[3/4] Finding dependency...
[4/4] Calculating file sizes...
=> Found "[email protected]"
info Reasons this module exists
   - "quasar-app-extension-ssg" depends on it
   - Hoisted from "quasar-app-extension-ssg#http-proxy-middleware"
info Disk size without dependencies: "380KB"
info Disk size with unique dependencies: "6.57MB"
info Disk size with transitive dependencies: "11.79MB"
info Number of shared dependencies: 30
=> Found "webpack-dev-server#[email protected]"
info This module exists because "@quasar#app#webpack-dev-server" depends on it.
info Disk size without dependencies: "80KB"
info Disk size with unique dependencies: "5.42MB"
info Disk size with transitive dependencies: "9.92MB"
info Number of shared dependencies: 28

Don't really know how and if this can be solved on your part, but at least now you know :)

Error when using Quasar Meta plugin

Hi there and thanks for your great work!

When using the Meta plugin of Quasar and your extension to statically generate the routes, I get an error in the browser, only for the routes that I have excluded from statically rendering.

The error reads:
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'remove')
and is caused by the following JS code:
document.getElementById("qmeta-init").remove())

Note here that the error appears even when there is no use of the Meta plugin in the specific page (or at all), just by activating it in the quasar.conf.js and the page doesn't even show up.

Steps to reproduce:

  1. Create a new project.
  2. Add the Meta plugin in quasar.conf.js.
  3. Create a new route.
  4. Exclude it from SSG.
  5. Build, serve and open the url of the above route in the browser.
  6. Optionally change the spa fallback to something other than 404.html to not get the 404 error.

how to pass data to routes before generate

Hi, I would like to switch nuxt2 -> quasar2 but I need SSG, we have hundred of routes to generates and we would know how to avoid spam the api (we need only 1 call to generate every pages)

With nuxt we use payload, how could we achieve the same with quasar + your ext ?

Extension(ssg) · ⚠️ ReferenceError: window is not defined

Hi thx for this great plugin! I have found the following issue. I am not sure where the problem lies. The SSG version seems to work just fine and I can serve it. But during generation I get this error:

✖ Extension(ssg) · Generating route... /
  › window is not defined
 Extension(ssg) · ⚠️    ReferenceError: window is not defined
  
  - server-bundle.js:1 Module.4edf
    server-bundle.js:1:12697
  
  - server-bundle.js:1 o
    server-bundle.js:1:194
  
  - server-bundle.js:1 Object.0
    server-bundle.js:1:1475
  
  - server-bundle.js:1 o
    server-bundle.js:1:194
  
  - server-bundle.js:1 module.exports.0
    server-bundle.js:1:1436
  
  - server-bundle.js:1 Object.<anonymous>
    server-bundle.js:1:1445
  
  - build.prod.js:1 o
    [comcharax-componardo]/[vue-server-renderer]/build.prod.js:1:77517
  
  - build.prod.js:1 
    [comcharax-componardo]/[vue-server-renderer]/build.prod.js:1:78110
  
  - new Promise
  
  - build.prod.js:1 
    [comcharax-componardo]/[vue-server-renderer]/build.prod.js:1:78018
  
  - build.prod.js:1 Object.renderToString
    [comcharax-componardo]/[vue-server-renderer]/build.prod.js:1:81553
  
  - ssr-config.js:60 Object.module.exports.renderToString
    [comcharax-componardo]/[.cache]/quasar-app-extension-ssg/ssr-config.js:60:12
  
  - generator.js:111 
    [comcharax-componardo]/[quasar-app-extension-ssg]/src/generate/generator.js:111:16
  
  - new Promise
  
  - generator.js:105 Generator.render
    [comcharax-componardo]/[quasar-app-extension-ssg]/src/generate/generator.js:105:12
  
  - generator.js:77 Generator.generate
    [comcharax-componardo]/[quasar-app-extension-ssg]/src/generate/generator.js:77:27

Done in 3.78s.

Any idea what this is about and where it comes from?

Does not refresh modified dependencies

I'm using this Quasar AppExtension to manage tags and meta.
I'm currently working on that AE so i added a local dependency to test my progress. I noticed that while in dev or ssr mode every time i modify the AE and i run yarn add -D ../path and quasar ext invoke @dreamonkey/meta everithing works just fine. With this AE i always need instead to delete my dist folder, uninvoke and delete that dependency, re add and re invoke it and finally run quasar ssg generate.
I also tried to delete my node modules and update them but nothing changes.
Any clues if i'm doing something wrong?

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Rate-Limited

These updates are currently rate-limited. Click on a checkbox below to force their creation now.

  • chore(deps): update dependency eslint to v9 (master)
  • chore(deps): update dependency eslint-config-airbnb-typescript to v18 (master)
  • chore(deps): update dependency eslint-plugin-lodash-template to v1 (master)
  • chore(deps): update dependency eslint-plugin-n to v17 (master)
  • chore(deps): update dependency husky to v9 (master)
  • chore(deps): update pnpm to v9 (master)
  • chore(deps): update typescript-eslint monorepo to v7 (master) (major) (@typescript-eslint/eslint-plugin, @typescript-eslint/parser)
  • fix(deps): update dependency cssnano to v7 (master)
  • fix(deps): update dependency http-proxy-middleware to v3 (master)
  • fix(deps): update dependency postcss-rtlcss to v5 (master)
  • fix(deps): update dependency webpack-dev-server to v5 (master)
  • chore(deps): update commitlint monorepo to v19 (2.x) (major) (@commitlint/cli, @commitlint/config-conventional)
  • chore(deps): update dependency commit-and-tag-version to v12 (2.x)
  • chore(deps): update dependency eslint to v9 (2.x)
  • chore(deps): update dependency eslint-plugin-lodash-template to v1 (2.x)
  • chore(deps): update dependency husky to v9 (2.x)
  • chore(deps): update node.js to v20 (2.x)
  • chore(deps): update npm to v10 (2.x)
  • fix(deps): update dependency ci-info to v4 (2.x)
  • fix(deps): update dependency destr to v2 (2.x)
  • fix(deps): update dependency globby to v14 (2.x)
  • fix(deps): update dependency open to v10 (2.x)
  • 🔐 Create all rate-limited PRs at once 🔐

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

Branch master
npm
package.json
  • @commitlint/cli ^17.8.1
  • @commitlint/config-conventional ^17.8.1
  • @typescript-eslint/eslint-plugin ^6.13.1
  • @typescript-eslint/parser ^6.13.1
  • commit-and-tag-version ^11.3.0
  • commitizen ^4.3.0
  • cz-conventional-changelog ^3.3.0
  • eslint ^8.54.0
  • eslint-config-airbnb-base ^15.0.0
  • eslint-config-airbnb-typescript ^17.1.0
  • eslint-define-config ^1.24.1
  • eslint-import-resolver-exports 1.0.0-beta.5
  • eslint-plugin-ecmascript-compat ^3.1.0
  • eslint-plugin-import ^2.29.0
  • eslint-plugin-lodash-template ^0.22.1
  • eslint-plugin-n ^16.3.1
  • husky ^8.0.3
  • pnpm-sync-dependencies-meta-injected ^0.0.10
  • typescript ^5.3.2
  • node >= v16.20.2
  • pnpm 8.11.0
  • magic-string ^0.30.0
  • svgo ^3.0.0
packages/quasar-app-extension-ssg/package.json
  • @freddy38510/vue-loader ^17.3.0
  • @freddy38510/vue-style-loader ^4.2.0
  • @quasar/render-ssr-error ^1.0.2
  • @quasar/ssl-certificate ^1.0.0
  • @quasar/ssr-helpers ^2.2.2
  • beastcss ^2.1.3
  • chalk ^4.1.2
  • chokidar ^3.5.3
  • compression ^1.7.4
  • cors ^2.8.5
  • crc ^4.3.2
  • cssnano ^6.0.1
  • destr ^2.0.2
  • dot-prop ^7.2.0
  • express ^4.18.2
  • fastq ^1.15.0
  • fs-extra ^11.2.0
  • globby ^13.2.2
  • hash-sum ^2.0.0
  • html-minifier ^4.0.0
  • html-webpack-plugin ^5.5.3
  • http-proxy-middleware ^2.0.6
  • kolorist ^1.8.0
  • lodash ^4.17.21
  • magic-string ^0.30.5
  • minimist ^1.2.8
  • node-html-parser ^6.1.11
  • open ^9.1.0
  • pony-cause ^2.1.10
  • postcss-rtlcss ^4.0.9
  • route-cache ^0.6.1
  • selfsigned ^2.4.1
  • semver ^7.5.4
  • serialize-javascript ^6.0.1
  • unplugin-auto-import ^0.17.1
  • vite ^2.9.16
  • webpack ^5.89.0
  • webpack-chain ^6.5.1
  • webpack-dev-server ^4.15.1
  • webpack-merge ^5.10.0
  • webpack-node-externals ^3.0.0
  • @quasar/app-vite ^1.7.0
  • @quasar/app-webpack ^3.12.0
  • @quasar/extras ^1.16.9
  • @types/fs-extra ^11.0.4
  • @types/node ^14.18.63
  • @vue/devtools ^6.5.1
  • cross-spawn 7.0.3
  • quasar ^2.14.1
  • vue ^3.3.9
  • vue-router ^4.2.5
  • workbox-build ^7.0.0
  • workbox-webpack-plugin ^7.0.0
  • @quasar/app-vite ^1.7.0
  • @quasar/app-webpack ^3.12.0
  • @quasar/extras ^1.16.9
  • @vue/devtools ^6.5.1
  • quasar ^2.14.1
  • vue ^3.3.9
  • vue-router ^4.2.5
playground/vite/package.json
  • @quasar/extras ^1.16.9
  • quasar ^2.14.1
  • vue ^3.3.9
  • vue-router ^4.2.5
  • @quasar/app-vite ^1.7.0
  • autoprefixer ^10.4.16
  • eslint ^8.54.0
  • eslint-config-prettier ^9.0.0
  • eslint-plugin-vue ^9.18.1
  • postcss ^8.4.31
  • prettier ^3.1.0
  • node ^18 || ^16 || ^14.19
playground/webpack/package.json
  • @quasar/extras ^1.16.9
  • core-js ^3.33.3
  • quasar ^2.14.1
  • vue ^3.3.9
  • vue-router ^4.2.5
  • @babel/eslint-parser ^7.23.3
  • @quasar/app-webpack ^3.12.0
  • eslint ^8.54.0
  • eslint-config-prettier ^9.0.0
  • eslint-plugin-vue ^9.18.1
  • eslint-webpack-plugin ^4.0.1
  • prettier ^3.1.0
  • node >= v14.21.3
Branch 2.x
npm
package.json
  • beastcss 2.1.1
  • bundle-runner 0.0.1
  • chalk 4.1.2
  • ci-info 3.8.0
  • compression 1.7.4
  • cors 2.8.5
  • crc 4.3.2
  • destr 1.2.2
  • express 4.18.2
  • fastq 1.15.0
  • fs-extra 11.1.0
  • globby 13.1.3
  • html-minifier 4.0.0
  • jiti 1.17.0
  • minimist 1.2.8
  • node-html-parser 6.1.4
  • open 8.4.1
  • pify 5.0.0
  • route-cache 0.4.7
  • selfsigned 2.1.1
  • semver 7.3.8
  • webpack-merge 5.8.0
  • workbox-build 6.5.4
  • @commitlint/cli 17.4.3
  • @commitlint/config-conventional 17.4.3
  • commit-and-tag-version 11.1.0
  • commitizen 4.3.0
  • cz-conventional-changelog 3.3.0
  • eslint 8.34.0
  • eslint-config-airbnb-base 15.0.0
  • eslint-plugin-import 2.27.5
  • eslint-plugin-lodash-template 0.21.0
  • husky 8.0.3
  • node >= 10.18.1
  • npm >= 6.13.4
  • yarn >= 1.21.1

  • Check this box to trigger a request for Renovate to run again on this repository

Exit Code is 0 for failing builds

I have a Quasar project and I am using this extension to generate SSG pages. The pages are uploaded to the AWS servers (S3 behind a CloudFront CDN) and everything is great!

Today, there was an error in one of my codes (used window without checking if process.env.SERVER is true for SSR). The error is not showing up during quasar build -m ssr build time. It shows up during generate phase via quasar ssg generate --force-build.

Apparently there is a bug that even a failed ssg generate will have an Exit Code of 0, which is standard for success. So, since my build script was relying on the Exit Code (i.e. echo $? in bash) of this generate script (0 being success, anything else being failure), it didn't stop and we found the issue only after it was uploaded to production.

I tested it manually to verify it's the issue -- for a compile/build time error, quasar build -m ssr is returning a non-zero Exit Code properly (e.g. 1 for a compilation error), but during the generate time, the extension doesn't return a non-zero Exit Code when it fails to generate the pages.

Failing build when providing a `build > distDir`

Hey there! Really glad you managed to make so much progress on this.
I tried to make the SSG work with a custom distDir but it fails due to this part of the extension:

// Set SSG distDir
if (!conf.build.distDir) {
conf.ssg.__distDir = join(api.appDir, 'dist', 'ssg')
}

Here you initialize the conf.ssg.__distDir option, but only if conf.build.distDir isn't provided.
Later on, you use it as input for join function, but if conf.build.distDir is set you'll get an undefined conf.ssg.__distDir.

I guess you meant to use conf.build.distDir as default value and provide a fallback when it's not defined.
You can accomplish this by changing the code to

conf.ssg.__distDir = conf.build.distDir || join(api.appDir, 'dist', 'ssg')

I tried it out on-the-fly and it fixes the problem with my project generation

[Question] Vue router links prevent lazy hydration? How to circumvent this?

sooo.. after the suggestion from here #86 to use lazy hydration (thank you @freddy38510 for the suggestion). I found this post here:

https://blog.logrocket.com/vue-3-lazy-hydration-from-scratch/

I am using a <LazyHydrate ssrOnly>{{productlist}}</LazyHydrate> wrapper for my component to prevent it from being rerendered through client-side hydration.

At first it didn't seem to work, But then I found out that pressing refresh button would actually do what I intend. Then I found out that it depends on how I get to the specified page with the component. When I arrive on the page using a vue-router link for example from the frontpage localhost:8000 it does NOT work and the Hydration happens anyway.

if I load the page directly through my web-server using an url like: localhost:8000/products. It will work as exected and only the components without the wrapper get hydrated.

the server logs also read as follows (using python3 -m http.server --directory dist/ssg/):

going through vue-router link:

Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
127.0.0.1 - - [28/Oct/2021 12:57:53] "GET / HTTP/1.1" 304 -
127.0.0.1 - - [28/Oct/2021 12:57:58] "GET /js/434.bd53fe9b.js HTTP/1.1" 304 -

going directly to the page:

127.0.0.1 - - [28/Oct/2021 12:58:35] "GET /products HTTP/1.1" 301 -
127.0.0.1 - - [28/Oct/2021 12:58:35] "GET /products/ HTTP/1.1" 304 -
127.0.0.1 - - [28/Oct/2021 12:58:35] "GET /css/app.b1ff5ca7.css HTTP/1.1" 304 -
127.0.0.1 - - [28/Oct/2021 12:58:35] "GET /js/vendor.fa97f680.js HTTP/1.1" 304 -
127.0.0.1 - - [28/Oct/2021 12:58:35] "GET /js/363.205c4d17.js HTTP/1.1" 304 -
127.0.0.1 - - [28/Oct/2021 12:58:35] "GET /js/app.62b9fde5.js HTTP/1.1" 304 -
127.0.0.1 - - [28/Oct/2021 12:58:35] "GET /css/vendor.630297ef.css HTTP/1.1" 304 -
127.0.0.1 - - [28/Oct/2021 12:58:35] "GET /js/434.bd53fe9b.js HTTP/1.1" 304 -
127.0.0.1 - - [28/Oct/2021 12:58:35] "GET /favicon.ico HTTP/1.1" 304 -

you can see that in the first case it only loads the main index and in the second case also the /products/index.html

I am not sure if I did everythin correctly here.. shouldn't ssg automatically load the static, rendered html from the respective folder? Or am I doing anything else wrong? Maybe in the setup of the ssg?

TypeScript Error: '_q' is defined but never used

Every time i invoke the AE it keeps rewriting the src/boot/bodyClasses.js (obviously unless i skip that step).
Doing so it will keep adding the _q variable that is never used throwing an error on build time.
I suggest something like:

export default ({ app }) => {
  queues.takeover.push((/*_q*/) => {
    const cls = getBodyClasses(client, app);

    if (client.is.ie === true && client.is.versionNumber === 11) {
      cls.forEach(c => document.body.classList.add(c));
    } else {
      document.body.classList.add.apply(document.body.classList, cls);
    }
  });
};

Move extension configuration to a `ssg` property on `quasar.config.js`

Configuration of this AE should probably be moved to a ssg property into quasar.config.js instead of relaying on ssr configuration.
Defaults could still be taken automatically by SSR configuration, the idea is only to allow people to override those without affecting SSR builds too.

In this way it would be easier for devs to generate different apps for ssg and ssr, instead of having to chose one of the 2.

This would also mean to provide adequate TS types augmenting QuasarConf and QuasarContext interfaces, to provide intellisense.

SyntaxError in combination with Quasar Meta Plugin

When using MetaPlugin, missing closing tag of <script> causes error.
This error doesn't not appear in spa mode.

To reproduce the error simply setup a new quasar project

quasar create

then add

framework: { plugins: [ 'Meta' ] } to quasar.conf.js and
import { createMetaMixin } from 'quasar' to Index.vue

and run

quasar ssg generate && quasar ssg serve dist/ssg

Environment & versions:

Build mode........ ssr
Pkg quasar........ v2.1.2
Pkg @quasar/app... v3.1.3
Pkg webpack....... v5
Debugging......... no
Transpiled JS..... yes (Babel)

Add minimal README

There are some things which aren't really clear to me, for which even just a link to an online article could be useful. I'll put them all here, in random order, then let's see what should be written on a README and what could be put into comments on the code.

  • with prerender-spa solution, there wasn't a css optimization step (at least, I didn't need to configure it in any way, if it was there), an explanation of what does criticalCss does, why preload is useful and the differences between different strategies could be useful
  • seems like criticalCss.preload should only be asked when criticalCss.enable is true, right? But right now it's asked always
  • fallback prompts confuse me: after the initial download of the page, shouldn't the SPA automatically be mounted? What do you mean by Enable fallback SPA then? I can see it's related to the "Not Found" page, but couldn't it just be a prerendered 404 page too, which then gets enhanced to a SPA?
  • what about 'bodyClasses' boot file? What's the problem it's solving?
  • could be useful to provide a reference about what options src-static/rendererOptions.js can be used to configure
  • more a confirmation than a question: ssrUpdate(), <script> tags deferring and data-server-rendered="true" addition, which had to be done manually when using prerender-spa solution, are already taken care by Quasar SSR, right?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.