Skip to content

Commit

Permalink
feat: Version 0.0.3
Browse files Browse the repository at this point in the history
  • Loading branch information
webJose committed Jul 24, 2023
1 parent 7c6ee8f commit 0c75a05
Show file tree
Hide file tree
Showing 6 changed files with 441 additions and 265 deletions.
10 changes: 10 additions & 0 deletions PublishNote.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
> This plug-in is in the early experimental stages. Feel free to provide feedback and even contribute.
## Changelog

### v0.0.3

+ Deleted the root options `includeImo` and `imoVersion` in favor of a single `imo` option.
+ Documented options' properties in JSDoc.
+ Made the `serverPort` option mandatory.
+ Moved the setting of Vite's `base` property to the `build` command to support working locally with `npm run preview`.
+ Added logging the resolved value of `base` so it is visible in the console when building or serving.
---
106 changes: 85 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,15 @@ export default defineConfig({
```

Except for the options, which are explained below, this should be it for root projects. For micro-frontend projects,
the file `src/spa.ts` needs to be created. This file becomes the main export of the project and should export the
the file `src/spa.ts` must be created. This file becomes the main export of the project and should export the
`single-spa` lifecycle functions.

## single-spa Root Projects

The `single-spa` *root project* (referred to as *root config* within the `single-spa` documentation) is the project
that loads all other micro-frontends and the one that has the `single-spa` package installed, and therefore the one
that calls `registerApplication()` and `start()`. The `single-spa` developers advertise as a best practice, to make
a root project that uses no framework. In other words, that your root project be devoided of all user interface
that loads all other micro-frontends and the one that has the `single-spa` package installed, and the one that
typically calls `registerApplication()` and `start()`. The `single-spa` developers advertise as a best practice, to
make a root project that uses no framework. In other words, that your root project be devoided of all user interface
elements. This is a view I don't share, and this plug-in is capable of making a suitable Vite + XXX root project. In
the end, the choice is yours.

Expand All @@ -70,8 +70,7 @@ export type SingleSpaRootPluginOptions = {
dev?: string;
build?: string;
};
includeImo?: boolean;
imoVersion?: string;
imo?: boolean | string | (() => string);
};
```

Expand All @@ -82,25 +81,85 @@ the import maps non-functional, at least for the native `importmap` type. The s
the import map script and the `import-map-overrides` package as first children of the `<head>` HTML element, as a post
action.

> The `import-map-overrides` package is injected using the **JSDelivr** network. In the future, this will become a
configurable option.
The `imo` option is used to control the inclusion of `import-map-overrides`. Set it to `false` to exclude it; set to
`true` to include its latest version from the **JSDelivr**. However, production deployments should never let unknown
versions of packages to be loaded without prior testing, so it really isn't good practice to just say "include the
latest version". Instead, specify the desired package version as a string. The current recommended version of
`import-map-overrides` is **v2.4.2** because **v3.0.0** (the latest at the time of this writing) doesn't work.

Set `includeImo` to `false` to not include `import-map-overrides`. If not specified or set to `true`, then the
`import-map-overrides` package is added to the HTML page, as long as there are import maps defined. Which version of
`import-map-overrides`? The one specified in the `imoVersion` property. If this is not specified, then the latest
version will be included. This is not a recommended setup for production grade deployments. For example, at the time
of this writing, the latest version (v3.0.0) doesn't work properly. Therefore, at the time of this writing, the
recommended version is `2.4.2`. Set this version property always, as a general recommendation.
```typescript
vitePluginSingleSpa({
type: 'root',
imo: '2.4.2'
})
```

Just like the case of the latest version, this will also use the **JSDelivr** network.

> **IMPORTANT**: Even if you request `import-map-overrides` to be included, it won't be included if no import maps
are present.

If you wish to change the source of the package from **JSDelivr** to something else, then provide a function that
returns the package's URL.

```typescript
vitePluginSingleSpa({
type: 'root',
imo: () => `https://my.cdn.example.com/[email protected]`
})
```

We finally reach the `importMaps` section of the options. Use this section to specify file names and the import map
type. The default behavior is to automatically import maps from the file `src/importMap.dev.json` whenever Vite runs
in `serve` mode (when you run the project with `npm run dev`), or the file `src/importMap.json` whenever vite runs in
`build` mode (when you run `npm run build`). Note, however, that if you have no need to have different import maps,
then you can omit `src/importMap.dev.json` and just create `src/importMap.json`. However, this is hardly ever the
case.
then you can omit `src/importMap.dev.json` and just create `src/importMap.json`.

#### Hiatus: Odd Behavior of Vite's `base` Configuration Property

The previous paragraph brings an important topic: Even when Vite's documentation clerly states that `base` is used
both while serving and building, it doesn't respect full URL's while serving. A full URL is a URL that starts with
the scheme (`http` or `https`). Any full URL specified as base, which is what `single-spa` projects need, is reduced
to its path.

Now, what if you want or need to specify a different file name? No problem. Use `importMaps.dev` to specify the
serve-time import map file; use `importMaps.buid` to specify the build-time import map file.
Because of this, this package's documentation has the following to say:

1. Please support the discussion at [Vite's GitHub repository](https://github.com/vitejs/vite/discussions/13927) by
upvoting it. It requests Vite's core team to study the possibility to respect full URL's (known in code as external
URL's) in all modes, not just `build`.
2. As a workaround for correct micro-frontend asset loading, use a set of import maps that use a compiled version of
your micro-frontends, and serve them using `npm run preview`. This requires that you first run `npm run build`.

Usually, the development import maps would look like this:

```json
{
"imports": {
"@learnSspa/spa01": "http://localhost:4101/src/spa.ts",
"@learnSspa/spa02": "http://localhost:4102/src/spa.ts"
}
}
```

This is because, while using `npm run dev`, no bundling takes place. If you decide that you cannot properly test or
develop your micro-frontend with the assets missing (images, fonts, etc.), then you'll have to use `npm run preview`
and this requires the import maps to point to the built, or bundled, `spa.js`:

```json
{
"imports": {
"@learnSspa/spa01": "http://localhost:4101/spa.js",
"@learnSspa/spa02": "http://localhost:4102/spa.js"
}
}
```

---

Back to configuring import maps...

What if you want or need to specify a different file name for your import maps? No problem. Use `importMaps.dev` to
specify the serve-time import map file; use `importMaps.buid` to specify the build-time import map file.

As seen in the TypeScript definition, you can specify the type of import map you want. The four choices are the four
possible options for the `import-map-overrides` package, and if not specified, it will default to
Expand All @@ -126,7 +185,7 @@ The plug-in options available to micro-frontend projects are dictated by the fol
*/
export type SingleSpaMifePluginOptions = {
type?: 'mife';
serverPort?: number;
serverPort: number;
deployedBase?: string;
spaEntryPoint?: string;
};
Expand All @@ -142,13 +201,18 @@ the import map will usually point to the micro-frontend's entry file with a full
`http://localhost:4444/src/spa.ts`, where `4444` is the server's port number. This URL host name and port values must
be set as the Vite project's `base` string for assets (images, fonts, etc.) to be properly served during development.

If `serverPort` is not specified, Vite's `base`, `server.port` and `preview.port` options will remain unconfigured; if
a server port number is specified, then all of the above will be configured.
> **IMPORTANT**: As already mentioned, `base` has an odd behavior that makes this base-setting exercise futile, so
please upvote the [GitHub discussion](https://github.com/vitejs/vite/discussions/13927) that wants to start a change
on this topic.

The `deployedBase` property is applied as Vite's `base` property during build (`npm run build`). Specify what makes
sense to your project. For example, a Kubernetes deployment under a single domain name would probably use path
prefixes for the individual micro-frontends, such as `/mifeA`. Use this property to specify this prefix.

> **IMPORTANT**: This version of the plug-in has code to set the base to `http://localhost:<server port>` if building
and `deployedBase` is empty. This has been made like this to support the workaround of working locally the
micro-frontend by means of building and previewing.

The last property, `spaEntryPoint`, has a default value of `src/spa.ts` and is used to specify the module that exports
all of the `single-spa`'s lifecycle functions (`bootstrap`, `mount` and `unmount`). If your entry module's file name
differs, use this property to specify it. Note that if your project is not using TypeScript, you'll still have to
Expand Down
39 changes: 19 additions & 20 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { HtmlTagDescriptor, IndexHtmlTransformResult } from 'vite';
import type { HtmlTagDescriptor, IndexHtmlTransformResult } from 'vite';
import type { Plugin, ConfigEnv, UserConfig } from 'vite';
import type { InputOption } from 'rollup';
import { promises as fs, existsSync } from 'fs';
import type { SingleSpaPluginOptions, SingleSpaRootPluginOptions, SingleSpaMifePluginOptions, ImportMap } from "vite-plugin-single-spa";
import { promises as fs, existsSync } from 'fs';

/*
NOTE:
Expand Down Expand Up @@ -110,28 +110,20 @@ export function vitePluginSingleSpa(config?: SingleSpaPluginOptions): Plugin {
if (!config) {
return cfg;
}
if ((config as SingleSpaMifePluginOptions).serverPort) {
cfg.server = {
port: (config as SingleSpaMifePluginOptions).serverPort
};
cfg.preview = {
port: (config as SingleSpaMifePluginOptions).serverPort
};
if (viteOpts.command === 'serve') {
// Development server.
cfg.base = `http://localhost:${(config as SingleSpaMifePluginOptions).serverPort}`;
}
}
cfg.server = {
port: (config as SingleSpaMifePluginOptions).serverPort
};
cfg.preview = {
port: (config as SingleSpaMifePluginOptions).serverPort
};
const assetFileNames = 'assets/[name][extname]';
const entryFileNames = '[name].js';
const input: InputOption = {};
let preserveEntrySignatures: false | 'strict' | 'allow-extension' | 'exports-only';
if (viteOpts.command === 'build') {
input['spa'] = (config as SingleSpaMifePluginOptions)?.spaEntryPoint ?? 'src/spa.ts';
preserveEntrySignatures = 'exports-only';
if ((config as SingleSpaMifePluginOptions).deployedBase) {
cfg.base = (config as SingleSpaMifePluginOptions).deployedBase;
}
cfg.base = (config as SingleSpaMifePluginOptions).deployedBase ?? `http://localhost:${(config as SingleSpaMifePluginOptions).serverPort}`;
}
else {
input['index'] = 'index.html';
Expand Down Expand Up @@ -172,13 +164,17 @@ export function vitePluginSingleSpa(config?: SingleSpaPluginOptions): Plugin {
injectTo: 'head-prepend',
});
}
if (!(cfg.includeImo === false) && importMap) {
const imoVersion = cfg.imoVersion ?? 'latest'
if (!(cfg.imo === false) && importMap) {
let imoVersion = 'latest';
if (typeof cfg.imo === 'string') {
imoVersion = cfg.imo;
}
const imoUrl = typeof cfg.imo === 'function' ? cfg.imo() : `https://cdn.jsdelivr.net/npm/import-map-overrides@${imoVersion}/dist/import-map-overrides.js`;
tags.push({
tag: 'script',
attrs: {
type: 'text/javascript',
src: `https://cdn.jsdelivr.net/npm/import-map-overrides@${imoVersion}/dist/import-map-overrides.js`
src: imoUrl
},
injectTo: 'head-prepend'
});
Expand All @@ -194,6 +190,9 @@ export function vitePluginSingleSpa(config?: SingleSpaPluginOptions): Plugin {
async config(_cfg, opts) {
return await configFn(opts);
},
configResolved(cfg) {
console.log('Configuration resolved. Base: %s', cfg.base);
},
transformIndexHtml: {
order: 'post',
handler(html: string) {
Expand Down
Loading

0 comments on commit 0c75a05

Please sign in to comment.