Using Vue Design System in Nuxt is throwing errors about export in system.js - javascript

I am trying to get the components imported into a Nuxt project, following the steps here:
https://github.com/viljamis/vue-design-system/wiki/getting-started#using-design-system-as-an-npm-module
Nuxt does not have a main.js (everything is plugin based), so what I have done is create a "plugin" and then do the import code in there like so (Nuxt recommends this for other libraries too and works fine):
vue-design-system.js
import Vue from 'vue'
import system from 'fp-design-system'
import 'fp-design-system/dist/system/system.css'
Vue.use(system)
Then in my config I do (removed other code in config):
nuxt.config.js
module.exports = {
css: [
{ src: 'fp-design-system/dist/system/system.css', lang: 'css' }
],
plugins: [
{ src: '~plugins/vue-design-system', ssr: true }
]
}
When I run npm run dev in my theme, it builds, but I get a warning:
WARNING Compiled with 1 warnings warning in
./plugins/vue-design-system.js 7:8-14 "export 'default' (imported as
'system') was not found in 'fp-design-system'
Seems to have an issue with the generated system.js regarding the export (the command npm run build:system).
In my page on screen I get the following error when trying to use a component in the design system:
NuxtServerError Cannot find module
'fp-design-system/src/elements/TextStyle' from
'/Users/paranoidandroid/Documents/websites/Nuxt-SSR'
If I hard refresh the page, I then get another message:
NuxtServerError render function or template not defined in component:
anonymous
Any idea what's happening here? It would be really great to get this working somehow.
At this current time, I'm not sure if it's a Nuxt issue or a Vue Design System issue. I think the latter, just because the Nuxt setup I have right now is very bare-bones...so it's not something else causing this.
Thanks.
Repository on GitHub:
Here is the repo for my "theme", but in order to get this going, you will need to create a design system separate from this with the same name and follow the steps to use the design system as a local (file) NPM module.
https://github.com/michaelpumo/Nuxt-SSR
console.log of system (from the JS import statement)

As for your first error (""export 'default' (imported as 'system') was not found in 'fp-design-system'"), the UMD built JS from vue-design-system does not export a "default" object. But you can simply workaround the issue by importing it as:
import * as system from 'fp-design-system'
instead of:
import system from 'fp-design-system'
Then another issue comes quickly as you noticed in your comments: "window is not defined", due again to the UMD built JS that expects window to be globally available, instead of the usual trick to use this (which equals window in a browser). Therefore as it is, the build is not comptible with SSR.
You could however slightly rework the built JS by replacing the first occurrence of window by this, but I am not sure if the result will still work.
Most probably you should better keep this module for client rendering only.

It seems Vue is looking for the ES6 pattern for importing module, which you should use for external javascript modules/files.
in ES6 it is
export default myModule
in ES5 it was
module.exports = myModule
Hope it will help.

Related

import inside Electron renderer script

I followed the Electron typescript quick-start code structure. Basically I cloned the repo.
It worked fine until I wanted to split my code in multiple .ts files, and import them in the renderer script.
Then I get the Uncaught ReferenceError: exports is not defined. Because of this line on top of the renderer.ts:
import { stuff } from "./otherfile.ts";
After digging for more info, it seems that the reason is the "module": "commonjs" from tsconfig... But if I change that to esnext then Electron will not load the preload script anymore!
Has anyone actually managed to get Electron and typescript fully working? I mean, with being able to use import across multiple files and stuff like that?
Minimal reproductible example:
file structure:
/dist
/dist/main.js
/dist/preload.js
/dist/renderer.js
/dist/stuff.js
/src
/src/main.ts
/src/preload.ts
/src/renderer.ts
/src/stuff.ts
/index.html
/src/main.ts:
import { ipcMain } from "electron"; // imports work fine in main
...
ipcMain.on(...
/src/preload.ts:
import { contextBridge, ipcRenderer} from "electron"; // imports work fine in preload
contextBridge.exposeInMainWorld("my-api", { ....
/src/renderer.ts
import stuff from "./stuff.ts"; // import fails in renderer (exports is not defined error)
/src/stuff.ts
const stuff = { ... };
export default stuff;
/index.html
<html>
...
<script src="./dist/renderer.js"></script>
</html>
ts.config:
if I change "module" to "es6", then preload.ts will not load.
if I leave "module" to "commonjs" then imports in renderer.ts not work
If I manually add var exports = {} in renderer.js after ts compiles the file, then I get a different error "require is not defined"
TL;DR
I believe your error is caused by confusing CommonJS (CJS) and ES modules (ESM). It sounds like you are using CJS exports with ESM imports which is not compatible. The next section is a working Election example and after that is a comparison of CJS and ESM exports (export) and require (import).
Working Solution
Your OP did not include any code so this is my best guess at what the issue was and provides a working solution. Start by setting up the code:
# Clone this repository
git clone https://github.com/electron/electron-quick-start-typescript
# Go into the repository
cd electron-quick-start-typescript
# Install dependencies
npm install
Next create the following file:
// src/otherfile.ts
export const stuff = {
fullOf: 'stuff'
}
export function shareLocation(loc: string): void {
console.log(`I was imported by: ${loc}`);
}
Now open the src/main.ts file and make the following changes:
// Add import to top of file.
import {stuff, shareLocation} from "./otherfile"
// Add this code to the end of the createWindow() function.
shareLocation('main.ts (main.js)');
Now if you run this example with npm start you will see I was imported by: main.ts (main.js) in your terminal.
If you tried to do this with the src/preload.ts file you will get an error because of sandboxing. To see this, make the following change in src/preload.ts:
// Add to first line.
import {stuff, shareLocation} from "./otherfile";
// Add after the for loop inside the DOMContentLoaded function.
shareLocation('preload.ts (preload.js)');
Now if run npm start you will get an error in the electron window (web browser) console. This is because of important security settings within Electron. Make the following change to your src/main.ts file:
// Modify the webPreferences of the new BrowserWindow.
const mainWindow = new BrowserWindow({
height: 600,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
sandbox: false // <=== ADD THIS LINE
},
width: 800,
});
Your import will work as intended now and you should see I was imported by: preload.ts (preload.js) in the electron window (web browser) console. Keep in mind this code is insecure! You should use IPC instead of disabling the sandbox.
If you are still getting an error I believe it is because your confusing CommonJS (CJS) and ES modules (ESM). It sounds like you are using CJS exports with ESM imports. See the next section that demos the difference.
CommonJS (CJS) vs ES modules (ESM)
CommonJS (CJS)
CommonJS was the original method for exporting and requiring (importing) modules in Node. It was a standard introduced by Mozilla engineer Kevin Dangoor in 2009. You would write some code in one file like so:
// example.js
function hello() {
console.log('World!');
}
module.exports = hello;
And then require (import) the code into another module/file like so:
// main.js
const hello = require('./example.js');
hello();
Just like ES modules, which is a newer standard, "you can export functions, var, let, const, and classes" (MDN Docs). Here is an bigger example that exports two functions using a single export object:
// example.js
function hello() {
console.log('World!');
}
function foo() {
console.log('Bar!');
}
module.exports = {
hello,
foo
};
We could even change the exported object to refer to our functions by a different name. We could change the export code to this for example:
module.exports = {
H: hello,
F: foo
};
We would then require (import) this code like so:
// main.js
const {hello, foo} = require('./example.js');
hello();
foo();
// OR with the other export example
const {H, F} = require('./example.js');
H();
F();
ES Module (ESM)
This is the modern approach to including JavaScript code from one file/module into another, and is designed to work in modern browsers. If you were manually writing JavaScript modules for the web you would have to add the type="module" attribute to the script tag that loads your module.
In your setup, and many others, you have bundlers (webpack for example) of some kind that handle this for you by compiling/transpiling your code for you. This topic is out of scope to this question though, so lets look at an example.
Using ESM our simple example now becomes:
// example.js
function hello() {
console.log('World!');
}
export default hello;
Notice how our export statement has changed. To keep things simple I provide a default export when exporting a single item. We can also inline the export:
// example.js
export default function hello() {
console.log('World!');
}
And then we import the code into another module/file like so:
// main.js
import hello from "example";
hello();
If you do not want to use default in your exported module you will have to use a different syntax for importing your code. As Darryl Noakes mentions, that would be Named Exports. Since this is a TypeScript project this includes changing your TypeScript config. This SO answer covers what to do if you want to go this route with this project.
If you export multiple items you do not have to use the default statement:
// example.js
function hello() {
console.log('World!');
}
function foo() {
console.log('Bar!');
}
export {
hello,
foo
}
And you import the code in a similar fashion to object destructing:
// main.js
import {hello, foo} from "example";
hello();
foo();
For additional help with import this MDN Doc is a great start as well as this Free Code Camp Article. JavaScript Tutorial has a great tutorial on what object destructing is. This SO Q&A goes more into module imports including why they are not actually using destructing. Thanks again to Darryl Noakes for pointing that out!
Sorry, it's not possible the way that boilerplate is configured.
To use import/require inside of the renderer process, you must either lower the default security settings in the latest versions of Electron OR use a transpiler so that the final code the renderer executes will not include import/require.
And in the electron-quick-start-typescript project you'll find this file that confirms that as the issue.
// This file is required by the index.html file and will
// be executed in the renderer process for that window.
// No Node.js APIs are available in this process unless
// nodeIntegration is set to true in webPreferences.
// Use preload.js to selectively enable features
// needed in the renderer process.
Node.js APIs include import and require.
Furthermore, when you do access require, if sandbox is enabled then you're actually getting a polyfilled version, not the underlying require - see these notes about preload script sandboxing
Your only other option short of changing boilerplates is to access anything you need via the preload script like window.api.yourFunction instead.
You may wish to use a boilerplate which includes transpilation via Webpack to enable you to use import inside of your renderer process.
I believe both of these boilerplates accommodate it (and I can probably advise you somewhat if you get stuck with one of them)
https://github.com/electron-react-boilerplate/electron-react-boilerplate (I recommend this one)
https://github.com/reZach/secure-electron-template
PS - If you're writing code intended for distribution to others, please do not enable nodeIntegration as it breaks the security model of Electron!
PPS - You can also try "module": "ES2020", which is what we use - but it will not fix your issue of not being able to import/require within the renderer process.
Electron doesn't fully support ECMAScript modules yet, but this should not impact your Typescript code, which can be very modern.
OPTION 1 (LOOSE FILES)
Do this in your index.html to bootstrap your renderer process's Typescript code, as in this initial sample of mine. This will get you up and running quickly, though it uses node integration, which should be disabled before you release to production:
<body>
<div id='root' class='container'></div>
<script type='text/javascript'>
require('./built/renderer');
</script>
</body>
OPTION 2 (BUNDLED)
To get rid of the require statement, and operate closer to an SPA, in line with Electron security recommendations, you can use webpack bundling and reference built Typescript as follows, as in this updated sample of mine:
<body>
<div id='root' class='container'></div>
<script type='module' src='vendor.bundle.js'></script>
<script type='module' src='app.bundle.js'></script>
</body>
SETTINGS
I specify type=commonjs in package.json and module=commonjs in the tsconfig.json file, whereas in non-Electron projects I use ECMAScript module settings instead, to build slightly better output.
The art of it of course is to produce a modern code setup that you can grow according to your own preferences, rather than being limited to what starter projects do.

Circular dependency (?) in library & nodejs library code - Object prototype may only be an Object or null: undefined

I'm using filestack-js in a Rails project which is bundled with Vite. Everything works as expected until I include the ESM module for the filestack-js library, in this case in a StimulusJS controller:
import { Controller } from "stimulus";
import * as filestack from "filestack-js";
export default class extends Controller {
// some irrelevant implementation code that calls filestack.init(...)
}
Loading the above controller file in the browser causes an error:
tslib.es6.js:25 Uncaught TypeError: Object prototype may only be an Object or null: undefined
at setPrototypeOf (<anonymous>)
at __extends (tslib.es6.js:25)
at http.ts:43
at node_modules/filestack-js/build/module/lib/request/adapters/http.js (http.ts:64)
at __init (chunk-IHTDASF6.js?v=1616a449:14)
at request_adapter.node.ts:17
This is an error produced by the browser while working in a development environment, using Vite to build and serve ES modules to the browser directly. It handles Typescript compilation. Removing the import * as filestack bit makes the error go away (but obviously breaks the class' functionality).
My google searches seem to suggest that this might be a circular dependency problem. The browser stack trace points towards a file in the filestack-js library:
// src/lib/request/adapters/http.ts
import * as url from 'url';
import * as zlib from 'zlib';
import Debug from 'debug';
import { AdapterInterface } from './interface';
import { getVersion } from '../../utils';
import * as Stream from 'stream'; // <---------- Stream imported here
import { FsRequestOptions, FsResponse } from '../types';
import * as utils from '../utils';
import { prepareData, parseResponse, combineURL, set as setHeader, normalizeHeaders } from './../helpers';
import { FsRequestErrorCode, FsRequestError } from '../error';
import { FsHttpMethod } from './../types';
const HTTPS_REGEXP = /https:?/;
const HTTP_CHUNK_SIZE = 16 * 1024;
const MAX_REDIRECTS = 10;
const CANCEL_CLEAR = `FsCleanMemory`;
const debug = Debug('fs:request:http');
class HttpWritableStream extends Stream.Writable {
// omitted class definition
}
Where Stream.Writable is actually undefined due to a circular dependency problem. I have no idea how that would happen or seem to only affect me.
This is not an issue that has been reported on the filestack-js issue tracker.
Debugging in the browser and cloning/linking the repository locally have confirmed that Stream.Writable is returning undefined, but I don't know enough about JS to understand why. Supposedly this typically happens due to a circular dependency, but I'm not sure how the nodejs Stream module would have circular dependencies on a random library like filestack-js. I am also inexperienced enough in the JS world to understand exactly what it means to be using a nodeJS library like Stream in a browser module - filestack-js has both browser modules and commonJS/nodeJS modules so I'm not sure how/if they relate or interact.
Here's what the Stream object looks like when logged to a browser console. Clearly something has been imported but Writable is not a property of what was imported:
FWIW this happens on Chrome and Firefox, latest versions of each.
I also tried using dpdm to analyze the filestack-js project for circular dependencies. It did find some but it doesn't appear as if they are causing errors, and it does seem to explicitly be excluding node libraries and other dependency libraries.
Ok I think I've solved my issue but I'm not an expert so I'll try to explain what the problem was. Feel free to chime in with clarification if you know better.
This was caused by filestack-js's heavy usage of nodejs libraries. Historically, Webpack v4 has polyfilled a lot of common NodeJS libraries for usage in-browser, entirely transparent to most developers. This worked great but was complete magic.
Rollup, and incidentally, Webpack v5, do not do this polyfilling, so any nodeJS libraries used by "ESM" libraries from NPM that aren't directly compatible with modern browsers will just break. In order to polyfill this manually I had to instruct Vite & Rollup to alias the name of the nodejs stream module to something that is directly compatible with browsers, and install that. To do that, I:
yarn add --dev stream-browserify
And added the following to my vite.config.js:
// ...
resolve: {
alias: {
stream: "stream-browserify",
},
},
// ...
There should be a very similar (but different) way of telling Rollup to do this, because here I do it though the Vite configuration.
For extra context, here is the GitHub issue I opened on the filestack-js repo: https://github.com/filestack/filestack-js/issues/458
Imported it directly as recommended in the link Taylor recommended.
import * as filestack from 'filestack-js/build/browser/filestack.esm';
https://github.com/filestack/filestack-js/issues/458#issuecomment-927373100

ES6 module, what counts as the first import?

Here's my module:
console.log("module imported");
export function call(){};
In main.ts:
import * as mod from './module';
// other code that doesn't use mod.
I would have expected this to log "module imported" to the console. In fact, the example seems pretty much the same as this one. And they say:
A module code is evaluated only the first time when imported
But there are no console logs. However, after the following edits to main.ts the log message appears:
import * as mod from './module';
if(false){
mod.call();
}
It would make sense if only the first time the module is actually used counted as the first import. But here the log message seems to be based on static analysis alone. The code path that uses the module is never executed.
How does this work? What counts as the first import of an ES6 module?
Also, my gut feeling says that this might be about the bundler. Does it optimize away an unused import like this? I'm running these code snippets in a react app, created with:
npx create-react-app my-app --template typescript
cd my-app
# add the module and import it to index.tsx
npm i
npm run start
# browser opens, check the console
On the other hand, the typescript react app also has imports like './index.css' and they are only there for the bundler to package them. It seems common to import something only for its side-effects.
I have searched for related questions but so far haven't found something with this specific problem:
Run ES6 code only if module is executed directly
`if __name__ == '__main__'` equivalent in javascript es6 modules
In browser JS code that imports from ES6 module is not executed
The last of these looks like a duplicate, but it is about a specific syntax error in the module resolution.
Your guess is correct, it's happening because of bundler. Its a feature of bundler known as Dead code elimination. To know more about it, search for Tree Shaking or Dead code elimination.
If you are not going to use anything from imported module, source code of module will not be included in your build.
I think create-react-app use Webpack for bundling. If you want to disable the feature, starting the app in development mode may solve it. BTW, its good to remove unused code while building.

Angular CLI 6: How to consume a library sub-project in the main application?

I've got an Angular 6 project that's made of up a main application, and a separate sub-project that is a library. I'm trying to consume this library from the main application, and I can't seem to get it working.
In the tsconfig.json, I have the following paths configuration:
"paths": {
"#my-company/my-package/*": "dist/my-package/*"
}
And then in the main app, I import the library like so:
import { ButtonModule } from '#my-company/my-package/button';
However, when I build the main application, I get tons of errors about not being able to find modules. For the above import statement, I'll get this error:
Module not found: Error: Can't resolve '/Users/jattardi/code/myproject/dist/my-package/button'
However, if I check the dist/my-package directory, there certainly is a button directory containing the type definitions.
The reason my imports have subpaths, e.g. #my-company/my-package/button instead of just #my-company/my-package is to make it tree-shakeable. Not sure this is possible. Since this is an Angular 6/ng-packagr generated build, do I lose this ability?
I was hit with the same error when trying to consume in the main application. My answer will not solve that case as I am trying to get my lazy loaded module, I moved into the new Angular6 library, to work.
I was able to publish my library project to npm and consume it in the main application as an npm dependency. For eg: #santony/ngx-material. May be as a workaround you could do that until this is resolved.
For those who are still facing this problem, there's a "how to" at ng-packagr's documentation:
https://github.com/ng-packagr/ng-packagr/blob/master/docs/secondary-entrypoints.md
Basically,
All you have to do is create a package.json file and put it where you want a secondary entry point to be created.
The contents of my_package/testing/package.json can be as simple as:
{
"ngPackage": {}
}
And also, in dev application, I've added the path reference to the secondary endpoint, so it worked:
"#my-company/my-library/another": [
"dist/my-library/another"
]

Using createjs-soundjs in react web app

I would like to use https://www.npmjs.com/package/createjs-soundjs to play sounds on a react web app.
I installed the package normally with npm install createjs-soundjs and it's showing in the packages list. How should I include this to my project, so that I could use it?
I have tried the following:
import React from 'react';
import classNames from 'classnames';
import createjs from 'createjs';
import SoundJS from 'createjs-soundjs'; // this line causes an error
The error message in console: soundjs-0.6.2.min.js:17 Uncaught ReferenceError: createjs is not defined. This error message is very clear, but I have no idea where to start. Am I missing something fundamental?
The SoundJS core bundle is not in the proper CommonJS or ES6 format, it's a bundle that makes strong assumption that it's loaded as a top-level script in the browser. So it first tries to create the global value by assigning it to a property on this (which it assumes to be the window) and then tries to access this global values as just createjs.
So obviously this doesn't work in the module context.
There's a workaround though, the code is for Webpack 2 and based on this comment (which is for Webpack 1):
module: {
rules: [
// ...
{
test: /createjs/,
use: [
'imports-loader?this=>window',
'exports-loader?createjs'
]
},
// ...
]
},
plugins: [
// ...
new webpack.ProvidePlugin({
'createjs': 'createjs',
}),
// ...
],
resolve: {
alias: {
'createjs': path.resolve(__dirname, '../lib/soundjs-0.6.2.combined'),
}
},
The 3rd entry (resolve.alias) maps the imports of createjs to the file I've downloaded and placed into my lib folder (which is not as elegant as using something from npm, but now I'm sure what I get. npm version might work as well).
The first entry (module.rules) tells Webpack to first substitute this with window, and then to take the createjs instance from the global (window) context and make it the module export.
Finally, the 2nd entry (ProvidePlugin) precedes all requests for global createjs (in other modules) with const createjs = require('createjs').

Categories

Resources