SvelteKit breaks npm's import mechanism - npm

I've written several npm library projects, and this is the way I import symbols in one JS file from another JS file, but it won't work in the script section of a svelte file:
My 'package.json' file has a name field (e.g. set to '#jdeighan/something`) and an 'exports' section with entries like "./utils": "./src/lib/utils.js". Then in any other JS file I can import symbols from utils.js with "import {somesymbol} from '#jdeighan/something/utils'. It's how to do imports from a library that you've installed with 'npm install', but it also (cleverly) works inside the project itself. But in a svelte file, this won't work - I get the error message "Failed to resolve import "#jdeighan/something/utils" from "src\routes+page.svelte". Does the file exist?". Here is what I have in my svelte file:
<script>
import {somesymbol} from '#jdeighan/something/utils';
</script>
I know that svelte has a handy $lib alias, but I'd prefer to use the npm standard mechanism, but it seems to be broken when using SvelteKit (not sure about using plain svelte)

I'd prefer to use the npm standard mechanism
This is absolutely not the standard mechanism. I have never seen people import from the current project by package name. While this is supported by Node itself, nothing else seems to support it, including e.g. the VS Code language server which will be unable to provide code navigation.
Using the name makes it less clear that the import is local and not a separate dependency and if the name were to be changed it would have to be adjusted everywhere.
I would recommend just not doing that. SvelteKit has $lib predefined as a default to provide essentially the same functionality in a convention-based way that actually works.

If you create a project with just these 3 files, then execute node foo.js in a console window, you get "Hello, World!":
package.json:
{
"name": "#jdeighan/something",
"type": "module",
"version": "1.0.0",
"exports": {
"./utils": "./utils.js"
}
}
foo.js:
import {message} from '#jdeighan/something/utils'
console.log(message);
utils.js
export let message = 'Hello, World!';

Related

SyntaxError: Cannot use import statement outside a module { AXIOS }

Hello I changed my code from fetch to axios and when I run my tests I get this problem... Can anyone help me with that ?
SyntaxError: Cannot use import statement outside a module
> 1 | import axios from "axios";
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.
By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".
Here's what you can do:
• If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/en/ecmascript-modules for how to enable it.
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs:
This issue occurred after axios update from 0.27.2 to 1.0.0. There is an opened issue, so probably this is going to be fixed soon. Meanwhile they're working on that, you can temporary fix it by setting up transformIgnorePatterns in jest config:
transformIgnorePatterns = ["node_modules/(?!axios)/"]
For me helped the following jest.config.js setup:
transformIgnorePattern: [
'<rootDir>/node_modules/(?!axios)/'
]
alongside with
moduleNameMapper: {
'^axios$': require.resolve('axios'),
}

DESO library NX building get's stuck + Import not found

I want to implement DESO into my app. The most convenient way is probably the DESO library from their developer hub.
I want to be as precise as possible here. So first I downloaded the deso-protocol npm package. In the readme of this library is stated that it is generated with nx. So I downloaded that too. No big issues. And I should: Run nx build deso-protocol to build the library.(in readme). Than I get an error that there is no workspace, when I want to create one with "npx create-nx-workspace deso-protocol --preset=core" , nx gets stuck in that process.
So I decided to do it without the building(I don't quite now if that is needed, am new to npm) and just do it with the classic js imports.
The "import Deso from 'deso-protocol';" from the docs isn't working. Because the module imports have to start with "/" or "./" or sth. similar etc.
So I tried import Deso from './node_modules/deso-protocol/src/index.js';. That's not working. Error:
Uncaught SyntaxError: import not found: default
using import { Deso } from './node_modules/deso-protocol/src/index.js'; doesn't work either because then Deso isn't found. I don't quite now what to do now.
Edit
I use plain JavaScript. deso-protocol uses plain JavaScript too, I hope. There isn't much code yet:
Index.js file:
import Deso from './node_modules/deso-protocol/src/index.js';
test.html file:
<script type="module" src="index.js"></script>
That's it. I use http.server from Python to host the local server.
Just install deso-protocol library from https://www.npmjs.com/package/deso-protocol with npm i deso-protocol
In your app import it as usual
import Deso from "deso-protocol";
And initialize
const deso = new Deso();
Readme file is really confusing, I have no idea what
Run nx build deso-protocol to build the library.
is supposed to mean, it works without that no problem.

Bundling a plugin with Rollup but having duplicate Vue.js package imported in the client app's bundle (Nuxt)

Dear Stack Overflow / Vue.js / Rollup community
This could be a noob question for the master plugin developers working with Vue and Rollup. I will write the question very explicitly hoping that it could help other noobs like me in the future.
I have simple plugin that helps with form validation. One of the components in this plugin imports Vue in order to programatically create a component and append to DOM on mount like below:
import Vue from 'vue'
import Notification from './Notification.vue' /* a very simple Vue component */
...
mounted() {
const NotificationClass = Vue.extend(Notification)
const notificationInstance = new NotificationClass({ propsData: { name: 'ABC' } })
notificationInstance.$mount('#something')
}
This works as expected, and this plugin is bundled using Rollup with a config like this:
import vue from 'rollup-plugin-vue'
import babel from 'rollup-plugin-babel'
import { terser } from 'rollup-plugin-terser'
import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
export default {
input: 'src/index.js',
output: {
name: 'forms',
globals: {
vue: 'Vue'
}
},
plugins: [
vue(),
babel(),
resolve(),
commonjs(),
terser()
],
external: ['vue']
}
As you can see, Vue.js is getting externalised in this bundle. The aim (and the assumption) is that the client app that imports this plugin will be running on Vue, therefore there's no need to bundle it here (assumption).
The very simple src/index.js that the bundler uses is below:
import Form from './Form.vue'
export default {
install(Vue, _) {
Vue.component('bs-form', Form)
}
}
Rollup creates 2 files (one esm and one umd) and references them in in the plugins package.json file like below:
"name": "bs-forms",
"main": "./dist/umd.js",
"module": "./dist/esm.js",
"files": [
"dist/*"
],
"scripts": {
"build": "npm run build:umd & npm run build:es",
"build:es": "rollup --config rollup.config.js --format es --file dist/esm.js",
"build:umd": "rollup --config rollup.config.js --format umd --file dist/umd.js"
}
Everything works as expected up to this point and the bundles are generated nicely.
The client app (Nuxt SSR) imports this plugin (using npm-link since it's in development) with a very simple import in a plugin file:
/* main.js*/
import Vue from 'vue'
import bsForms from 'bs-forms'
Vue.use(bsForms)
This plugin file (main.js) is added to nuxt.config.js as a plugin:
// Nuxt Plugins
...
plugins: [{src: '~/plugins/main'}]
...
Everything still works as expected but here comes the problem:
Since the clients is a Nuxt app, the Vue is imported by default of course but the externalised Vue module (by the forms plugin) is also imported in the client. Therefore there is a duplication of this package in the client bundle.
I guess the client app can configure its webpack config in order to remove this duplicated module. Perhaps by using something like a Dedupe plugin or something? Can someone suggests how to best handle situation like these?
But what I really want to learn, is the best practice of bundling the plugin at the first place, so that the client doesn't have to change anything in its config and simply imports this plugin and move on.
I know that importing the Vue.js in the plugin may not be a great thing to do at the first place. But there could be other reasons for an import like this as well, for example imagine that the plugin could be written in Typescript and Vue.js / Typescript is written by using Vue.extend statements (see below) which also imports Vue (in order to enable type interface):
import Vue from 'vue'
const Component = Vue.extend({
// type inference enabled
})
So here's the long question. Please masters of Rollup, help me and the community out by suggesting best practice approaches (or your approaches) to handle situations like these.
Thank you!!!!
I had the same problem and I found this answer of #vatson very helpful
Your problem is the combination of "npm link", the nature of nodejs module loading and the vue intolerance to multiple instances from different places.
Short introduction how import in nodejs works. If your script has some kind of library import, then nodejs initially looks in the local node_modules folder, if local node_modules doesn't contain required dependency then nodejs goes to the folder above to find node_modules and your imported dependency there.
You do not need to publish your package on NPM. It is enough if you generate your package locally using npm pack and then install it in your other project npm install /absolute_path_to_your_local_package/your_package_name.tgz. If you update something in your package, you can reinstall it in your other project and everything should work.
Here is the source about the difference between npm pack and npm link https://stackoverflow.com/a/50689049/6072503.
I have sorted this problem with an interesting caveat:
The duplicate Vue package doesn't get imported when the plugin is used via an NPM package (installed by npm install -save <plugin-name> )
However, during development, if you use the package vie npm link (like npm link <plugin-name>) then Vue gets imported twice, like shown in that image in the original question.
People who encounter similar problems in the future, please try to publish and import your package and see if it makes any difference.
Thank you!

ES6 import and npm packages

Anyone got a link to documentation explaining exactly how ES6 import is supposed to work with npm packages (as opposed to javascript module files) ?
Also (and possibly related) a link to documentation on the use/significance of "module" as a top-level key in an npm package.json file ?
Eg. consider the following from juggle/resize-observer ;
package.json
{
...
"module": "lib/exports/resize-observer.js",
...
}
consumer javascript file
import { ResizeObserver } from '#juggle/resize-observer';
On the above import Chrome sends a request to the server for '#juggle/resize-observer' which is a directory..... so what happens next ? (I don't have a working instance of this and my server doesn't return anything yet as I don't know what it's supposed to / in what format ). Is this syntax only for use with some sort of build tool where it ultimately gets replaced with the actual file ?
I have looked at the npm site/googled and cannot find any relevant documentation.
UPDATE Still waiting for a link to relevant documentation (no relevant Google results at this time) but for anyone asking the same question: apparently you need your server to do "module resolution" . Alternatively you can use Snowpack or a bundler eg. Webpack.
Apparently npm/node packages are not packaged with browsers in mind or based on any W3C/Web Modules standard specification.
See here for module resolution info.
So at present to use an npm package in a browser you must do one of the following
use a bundler like webpack
use snowpack
use a CDN which resolves the module request
implement npm module resolution in your own server
Find the package entry point and use that in your ES6 import statement.
However, if the package's json "main" property changes in a subsequent update you
will need to update your code to reflect this.

How to import a node_modules dependency for use in a frontend ES6 module

I'm playing around with ES6 syntax and would like to build a small module in NPM, which uses another npm module (e.g. push-js) as a dependency. Currently, I'm using rollup to bundle and generate my distribution files.
I'm not sure what's the right way to include a dependency in order to use it in my own module. This is what I tried
import * as Push from 'push.js';
class _MyModule
{
Push.create("Go ahead, click this notification", {
});
}
Rollup triggers the following error on this code:
events.js:160
throw er; // Unhandled 'error' event
^
Error: Unexpected token
Am I doing something fundamentally wrong here?
You are close enough. However, at least in current transpilers (Babel and co.), CommonJS module exports are treated like default exports. Meaning, instead of importing all separate entities (import * as Push), you only have to import the default exports (import Push).
import Push from 'push.js';
class _MyModule
{
constructor() {
Push.create("Go ahead, click this notification", {
});
}
}
How the actual interoperability between CommonJS and ES Modules will be solved, is not yet finalized. See Axel Rauschmayr's blog post on the subject.