Openlayers in nuxt blob is undefined - javascript

I'm trying to make openlayers work in Nuxt but whenever I tried to import openlayers components, I had several errors that I solved, but one of them is "Blob is not defined - node_modules/ol/worker/webgl.js"
I found nothing on openlayers and nuxt and i'm having hard times to just make it work :/
Here is the steps of what I did :
npm install ol
made a file with import View from 'ol/View', got error "can't import ESM module...."
created a plugins folder with a ol.js with all OL assests imported, and added plugins: ['#/plugins/ol'] in nuxt.config
preview of my ol.js file in my plugins folder
Got error "can't read fs" file
added extend: (config, { isDev, isClient }) => { config.node = {fs: 'empty',} into my nuxt.config file in build
Also added standalone: true,
and NOW I have blob is undefined and really, I have no clue on what to do to make openlayers work :/
Any help is welcome !
EDIT : Made some changes
1 I installed vuelayers
2 followed the guide on https://vuelayers.github.io/#/docs/quickstart?id=nuxtjs
edited nuxt.config
plugins: [{
src: '#/plugins/vuelayers.js',
ssr: false
}, { ... }],
modules: [
...,
'~/shared/vueLayers',
],
Create a file shared/ directory named vuelayers.js
export default function (moduleOptions) {
this.options.css.push('vuelayers/lib/style.css')
}
3 pasted the content of the "simple map example"
I have no error but nothing is displayed on my component yet

The "can't import ESM module...." occurs because the ol package exports an ES6 module and when Nuxt is rendered on the server side the parent project uses CommonJS modules. As a result a run time error occurs when the open layers code is not transpiled for server side rendering.
I found there to be two solutions to this problem.
Explicitly transpile the Open Layers modules that are used in the transpile property of the build property in nuxt.config.js
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {
transpile: [
'ol/control',
'ol/proj',
'ol/style/Circle',
'ol/style/Fill',
'ol/format/GeoJSON',
'ol/format/MVT',
'ol/Map',
// ...
],
},
Create a Nuxt plug-in to wrap Open Layers that's only used on the client side similar to the example seen in this gist.
I found the second solution to be cleaner and since Open Layers uses <canvas> render the map it can't easily be rendered on the server side anyway.
Note that the Gist linked above is a bit dated, but the idea is still relevant. A modern example might look like the following:
// plugins/open-layers.js
import Map from 'ol/Map';
import View from 'ol/View';
export default (context, inject) => {
const ol = {
Map,
View,
};
inject('ol', ol);
};
// nuxt.config.js
export default {
// ...
plugins: [
{ src: '~/plugins/open-layers.js', mode: 'client' },
],
};
// Parent component
<template>
<client-only>
<Map />
</client-only>
</template>
// Map.vue
<template>
<div ref="map" />
</template>
<script>
export default {
name: 'Map',
methods: {
renderChart() {
this.map = new $ol.Map({
target: this.$refs.map,
view: new $ol.View({}),
});
},
},
};
</script>

Related

TypeError: multi is not a function / default import from rollup-plugin-multi-input does not work from ESM package

I found a plugin rollup-plugin-multi-input which fixes the problem of not being able to specifiy a glob to the test rollup config. For the unt tests, the entry point is not a single entity from which an import graph can be derived. It is just a collection of source files containing tests, which doesnt fit input requirement of rollup.
However, my attempt at trying to use it was fruitless:
rollup-config.tests.mjs:
import multi from 'rollup-plugin-multi-input';
const testConfig = {
input: ['test/**/*.spec.ts'],
external: ["chai", "mocha", "dirty-chai"],
output: {
format: "es",
file: `dist/${name}-bundle.test.js`,
plugins: [],
sourcemap: true
},
plugins: [
multi(),
resolve(),
commonjs(),
typescript({
tsconfig: "./tsconfig.test.json"
})
],
}
just resulted in this error:
[!] TypeError: multi is not a function
TypeError: multi is not a function
Looking at the exported code from the plugin, I can see that the default export is a function:
var _default = function(param) {
}
exports.default = _default;
So I don't know why this doesnt work.
I since discovered that there is another plugin that does a similar thing: #rollup/plugin-multi-entry:
import entry from "rollup-plugin-multi-entry";
plugins: [
entry(),
resolve(),
commonjs(),
typescript({
tsconfig: "./tsconfig.test.json"
})
],
so configured and invoked in exactly the same way, but now it works in the way that I wanted it to; the test bundle is created and mocha indeed sees all the tests and executes them successfully.
So let's take a look at that export and see if there is a difference in what is exported:
Well the first thing to notice is that its dist folder contains a .mjs file and a .js file. Since we're importing from an ESM package ("type": "module" in package.json), I guess we're using the default export from the .mjs file:
function multiEntry() {
...
}
export default multiEntry;
With rollup-plugin-multi-input, I even tried using the createRequire from "module":
import { createRequire } from "module";
const require = createRequire(import.meta.url);
const multi = require('rollup-plugin-multi-input');
but that failed for the same reason.
So whats the problem here? Why does default import from rollup-plugin-multi-input not work?

How to set up plotly.js in nuxt SSR?

I'm trying to set up plotly.js in nuxt but whatever I do I get this cryptic error
self is not defined
I tried to install plotly.js and plotly.js-dist same error shows.
I would prefer to make custom build so I tried like this in nuxt plugins:
// here we use custom partial bundle
import plotly from 'plotly.js/lib/core';
import barpolar from 'plotly.js/lib/barpolar';
export default function (_, inject) {
plotly.register([barpolar]);
inject('plotly', plotly);
}
but whenever I register nuxt plugin site crashes with aforementioned error.
Even not going down custom bundle route, and using dist lib still fails just the same.
I also tried not to employ nuxt plugins system but to import manually and to set up, same things happen.
I also added ify-loader as recommended here: https://github.com/plotly/plotly-webpack
and this is my nuxt.config.js in regards to webpack plugin:
build: {
extend(config, { isClient }) {
console.log('config :>> ', config);
config.module.rules.push({
test: /\.js$/,
use: [
'ify-loader',
'transform-loader?plotly.js/tasks/compress_attributes.js',
],
});
},
},
still no luck.
I presume this is problem with webpack 5 and plotly.js not working well together in default setup but I have no idea how to solve this.
Help appreciated.
The reason why this wasn't working is that plotly tried to access document, and in SSR that would obviously fail.
So to fix this I just had to assign plugin in client only mode like this:
plugins: [
// other plugins
{ src: '~/plugins/plotly', mode: 'client' },
],
and it worked.

Bundle JS and CSS into single file with Vite

I'm having a devil of a time figuring out how to build a single .js file from Vite in my Svelte project that includes all of the built javascript and CSS from my Svelte projects. By default, Vite bundles the app into one html file (this is ok), two .js files (why??), and one .css file (just want this bundled into the one js file).
I ran this very basic command to get a starter project:
npx degit sveltejs/template myproject
I tried adding a couple of plugins, but nothing I added achieved the results I wanted. Primarily, the plugins I found seem to want to create a single HTML file with everything in it. It seems like PostCSS might be able to help, but I don't understand what configuration I can set via Vite to get it to do what I want.
What is the magic set of plugins and config that will output a single HTML file with a single js file that renders my Svelte app and its CSS onto the page?
Two steps,
We can inject css into js assets with vite-plugin-css-injected-by-js.
We can emit a single js asset by disabling chunks in rollup's config.
Final result,
import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
export default defineConfig({
plugins: [cssInjectedByJsPlugin()],
build: {
rollupOptions: {
output: {
manualChunks: undefined,
},
},
},
});
If you're looking for a solution to this, you might want to take a look at vite-plugin-singlefile.
That doesn't come out of the box for vite but you can write a quick plugin which will be doing exactly that
const bundle_filename = ''
const css_filename = 'style.css'
defineConfig({
build: {
lib: {
entry: 'src/mycomponent.js',
name: 'mycomponent.js',
fileName: () => 'mycomponent.js',
formats: ['iife'],
},
cssCodeSplit: false,
rollupOptions: {
plugins: [
{
apply: 'build',
enforce: 'post',
name: 'pack-css',
generateBundle(opts, bundle) {
const {
[css_filename]: { source: rawCss },
[bundle_filename]: component,
} = bundle
const IIFEcss = `
(function() {
try {
var elementStyle = document.createElement('style');
elementStyle.innerText = ${JSON.stringify(rawCss)}
document.head.appendChild(elementStyle)
} catch(error) {
console.error(error, 'unable to concat style inside the bundled file')
}
})()`
component.code += IIFEcss
// remove from final bundle
delete bundle[css_filename]
},
},
],
},
},
})
I created a boilerplate Vite project for this problem:
https://github.com/mvsde/svelte-micro-frontend
Maybe the configuration helps with your case:
import { defineConfig } from 'vite'
import { svelte } from '#sveltejs/vite-plugin-svelte'
export default defineConfig({
plugins: [
svelte({
emitCss: false
})
],
build: {
assetsDir: '',
sourcemap: true,
lib: {
entry: 'src/main.js',
formats: ['iife'],
name: 'SvelteMicroFrontend',
fileName: 'svelte-micro-frontend'
}
}
})
I had the same issue and was able to fix by editing vite.config.ts as follows tested on vite#2.3.8
export default {
build: {
rollupOptions: {
output: {
manualChunks: undefined,
},
},
},
};
If you are working with Svelte, you can use emitCss:
export default defineConfig({
plugins: [svelte({
emitCss: false,
})],
})
As manualChunks are no longer working in a latest versions of Vite, there's no any way to combine all the chunks into one.
But found a hacky solution to have an index.html + bundle.js after the build: https://github.com/d-velopment/SvelteKit-One-Bundle - it rewraps the project's initial .js files to go from bundle.js, which could be loaded from index.html or external project.

How to dynamically import markdown files

I'm building a blog using Vue, where I'd like my posts to be written as markdown files.
Currently I have the following structure
src/
posts/
blogpost1.md
blogpost2.md
view/
myComponent.vue
I want myComponent.vue to dynamically load a markdown post based on the route params.
For instance if we visited .../blog/blogpost2, then we would dynamically load in blogpost2.md
My current implementation is as follows:
<template>
<div
v-html="md"
></div>
</template>
<script>
import marked from "marked";
export default {
data () {
return {
md: undefined,
};
},
created () {
const importRoute = `posts/blog/${this.$route.params.postName}.md`;
const md = require(importRoute);
this.md = marked(md);
},
};
</script>
To load the markdown file I'm using markdown-loader in webpack.
I'm also using copy-webpack-plugin to try to copy my entire /posts dir into my build folder.
configureWebpack: {
module: {
rules: [{
test: /\.md$/,
use: [
{ loader: "html-loader", options: {} },
{ loader: "markdown-loader", options: {} },
],
},
],
},
plugins: [
new CopyPlugin({
patterns: [
{ from: "src/posts", to: "posts" },
],
}),
],
},
When I run npm run serve however, I receive
[Vue warn]: Error in created hook: "Error: Cannot find module './posts/blogpost2.md'"
And when I look at the pages in my browser I don't see the posts directory (see image below)
However, when running npm run build:staging I do see the posts directory in /dist
Why am I unable to import import blogpost2.mddynamically?
My attempt at using a dynamic import that changes based on the route will not work because webpack is unable to properly bundle the imported files. This is because webpack needs to know what it will be importing in order to bundle them correctly.
Instead we can use a different approach of copying our markdown directory as it is into /dist and sending a request to fetch each markdown file.
We end up with this in our webpack config (or in my case vue.config.js):
const CopyPlugin = require("copy-webpack-plugin");
configureWebpack: {
plugins: [
new CopyPlugin({
patterns: [
{ from: "src/posts", to: "posts" },
],
}),
],
},
Note that we don't need the webpack loaders shown in my initial question because webpack itself isn't handling the loading of these files.
This ensures that posts/ gets copied as it is when we build this project.
Then in our component we can dynamically retrieve Markdown files like so:
<template>
<div
v-html="md"
></div>
</template>
<script>
import marked from "marked";
export default {
data () {
return {
md: undefined,
};
},
async created () {
const res = await fetch(`http://localhost:8081/posts/${this.$route.params.postName}.md`);
const md = await res.text();
this.md = marked(md);
},
};
</script>
// I once need to do a similar thing and faced similar challenges but it was built with Create React App, so please take my answer with a pinch of salt.
Try placing your import path inside require(…) (or import(…), if you ever use it) instead of declaring a variable then passing it in. I remember WebPack has trouble finding files when the paths are not static strings (hard-coded in this way). Whatever dynamic path it may be, your path needs to start with a static string, but you can concatenate dynamic values behind it.
Also, notice the ./ I added to the front of the path. Ignore it if you're using absolute imports, otherwise, this might also be a reason why the code failed?
created () {
// Instead of:
// const importRoute = `posts/blog/${this.$route.params.postName}.md`;
// const md = require(importRoute);
// Try:
const md = require(
`./posts/blog/${this.$route.params.postName}.md`
);
this.md = marked(md);
},
Extra: As much as I know, that the created hook in Vue is supposed only be synchronous, but after stumbling upon this answer I decided to include this snippet too. This one uses dynamic import. If all else fails perhaps you can give this a try?
async created () {
const { default: url } = await import(
`posts/blog/${this.$route.params.postName}.md`
);
const res = await fetch(url);
const md = res.text();
this.md = marked(md);
},

Bundle Handsontable into Aurelia application

I'd like to know how to correctly bundle handsontable community edition library into Aurelia application. Handsontable is distributed as single JS file packed as webpack module, thus a hint may be useful for webpack modules generally.
Having Aurelia version 0.24.0 configured with default loader: RequireJS and default transpiler: Babel, NPM version 3.10.0. I did following steps in order to bundle handsontable into my application:
npm install handsontable
then edited aurelia-project/aurelia.json to add the following
dependencies:{ ...
{
"name": "handsontable",
"path": "../node_modules/handsontable",
"main": "dist/handsontable.full",
"resources": [
"dist/handsontable.full.css"
]
},
Then the component looks like: (view - htable.html):
<template>
<div ref="filetable"></div>
</template>
(viewmodel - htable.js in ES6):
import {Handsontable} from "handsontable";
export class htable {
...
attached() {
this.ht = new Handsontable (this.filetable, {
data: this.data,
rowHeaders: true,
colHeaders: true
});
}
}
However, any attempt to add such component fails with
Unhandled rejection TypeError: _handsontable is undefined
Another attemp I use in different library produce similar error:
import * as Handsontable from "handsontable";
...
this.ht = new Handsontable.Handsontable (this.filetable, {
Btw. A workaround is not to bundle and add handsontable into index.html which makes "Handsontable" global.

Categories

Resources