Using node subpath exports in Nuxt - node.js

I have a local repo(called repo-components) with some vue components and a package.json where I want to implement subpath exports, like so:
{
"name": "repo-components",
"version": "1.0.0",
"description": "Local repo",
"exports": {
"./components/": "./src/components/"
},
}
I want to then use that repo inside a Nuxt project(let's call it nuxt-project) as a dependency, and then use subpath exports like so inside nuxt-project:
import MyButton from 'repo-components/components/MyButton.vue'
However, when I try to do that import inside nuxt-project, it's not recognizing the subpath export I defined and simply tries to go directly to that path I defined(which doesn't exist). But if I try to do import MyButton from 'repo-components/src/components/MyButton.vue' then that works.
And if I try to use repo-components inside a simple node project and use require instead of import, then the exports field works perfectly, so clearly I'm missing some sort of configuration inside the Nuxt project.
Any help?

Related

Newly made NPM package is found, but deeper import references are all unfound

This is my first go publishing an NPM package and I feel like a newb. My basic imports aren't working, both within the module itself and when trying to reference specific files within the package from outside. The whole npm publish -> npm install part works as expected.
The file structure is a ./lib directory, with a ./lib/data-types directory. The main files with the objects getting exported live in the lib, and some other "helper" files live in the data-types.
- index.js, etc
- /lib
-- connection.js
-- session.js
-- /data-types
--- point.js, etc
I have an index.js file that's just a passthrough for some other objects:
import Connection from "./lib/connection.js"
import Session from "./lib/session.js"
export default {
Connection,
Session,
}
And I've defined the main export and the data-types in package.json:
{
"name": "ef-vue-crust",
"type": "module",
"main": "index.js",
"exports": {
"." : "./index.js",
"./data-types/": "./lib/data-type/*.js"
},
...
}
The basic import from my application seems to work, i.e. import {Connection} from 'ef-vue-crust', except for the aforementioned inner disconnect. index.js is unable to find the following files:
import Connection from "./lib/connection.js"
import Session from "./lib/session.js"
Module not found: Error: Can't resolve './lib/session.js' in 'C:\Projects\my-app\node_modules\ef-vue-crust'
Directly importing a file from the ./lib/data-type/ directory has the same issue in my application:
import Point from '#ef-vue-crust/data-types/point.js';
Does anyone see the disconnect?
Part 1: changed export default {} to export {} in index.js.
Part 2: looks like I was missing an * in the exports:
{
"name": "ef-vue-crust",
"type": "module",
"main": "index.js",
"exports": {
"." : "./index.js",
"./data-types/*": "./lib/data-type/*.js"
},
...
}
And finally: I had some strings flat out not matching in the imports, which became obvious once the above was fixed.
So I suppose the answer is "attention to detail"

Node.js 14 ES module import from another folder

This is a FYI and a question.
I am using Node.js 14.17.3 (LTS) and wanted to take advantage of using import ES syntax to use some files in another project (which need to remain where they are).
So, I made a simple node.js app to test the import. I added "type": "module" to the package.json file in the test app folder.
The (cut down test) file mixin-modules-definitions.js to import is:
const classModuleDefs = {
name: 'mydef'
}
export default classModuleDefs
Now, if I have that file in the same folder as the package.json file of my test app, this works fine:
import classModuleDefs from './mixin-modules-definitions.js'
console.log('classModuleDefs=', classModuleDefs)
But if I try to import from where the file actually lives like the following, I get the dreaded SyntaxError: Unexpected token 'export' error.
import classModuleDefs from '../src/services/mixin-modules-definitions.js'
And, if I then add "type": "module" to the nearest package.json file relevant to that path, the error goes away and the file is imported correctly.
As it turns out, the app I am importing from is ok with adding "type": "module" to its package.json file but I could imagine a scenario where it might not be ok.
Is it possible to import a file and have node resolve to use the package.json in my test node app without needing to also change the package.json in the "source" app.
Thanks,
Murray

Package.json with multiple entrypoints

I have a library which was used as follows
import { Foo } from '#me/core';
const foo = new Foo();
The package.json of that library looks like
"name": "#me/core",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"license": "MIT",
...
With dist/index.js its entrypoint. Now, however, I would provide an import for NodeJs projects only and one for web projects. Ideally I would have something like this for NodeJs
import { Foo } from '#me/core/nodejs';
and if you're working on a web-project you could do
import { Foo } from '#me/core/web';
My understanding is that both #me/core/nodejs and #me/core/web will be different NPM packages this way, which is not what I want. I want it to be in 1 npm package.
I tried to changed the library's index.ts file, from
export * from './foo';
into
import * as nodejs from './nodejs';
import * as web from './web';
export { web, nodejs };
This actually works, but I have to use it now (in a NODEJS project) as follows
import { nodejs } from '#me/core';
const foo = new nodejs.Foo();
Is there maybe a way to import this such that I don't need the nodejs everytime?
As you can see I'm not so sure what I should do here so any help would be appreciated!
UPDATE: Based on the suggestions by #Klaycon I see the following error:
As you're using ECMAScript modules, please refer to node.js docs on package entry points:
In a package’s package.json file, two fields can define entry points for a package: "main" and "exports". The "main" field is supported in all versions of Node.js, but its capabilities are limited: it only defines the main entry point of the package.
The "exports" field provides an alternative to "main" where the package main entry point can be defined while also encapsulating the package, preventing any other entry points besides those defined in "exports". This encapsulation allows module authors to define a public interface for their package.
So, in your package.json, you could define exports like this:
"main": "dist/index.js",
"exports": {
".": "./dist/index.js",
"./nodejs": "./dist/nodejs",
"./web": "./dist/web",
}
The accepted answer, using the exports field, is one way to do it, but not the only way -- and as you've found, isn't currently supported by Typescript.
Look at the #angular/material package, which works similarly to what you have in mind. In #angular/material/package.json, you've got a module field that points to an empty JS file, and a typings field that points to an empty .d.ts file. But you've also got #angular/material/button/package.json which points (indirectly) to the implementation of MatButton (and a bunch of other stuff). When you import {MatButton} from "#angular/material/button", it resolves this package file, looks at its module and typings fields, and uses those to resolve the runtime code and exported types, respectively.
You could do the same thing: #me/core/package.json would have module and typings fields that point to empty files; #me/core/node/package.json would point to code for your Node-specific Foo class, and #me/core/web/package.json would point to code for your browser version of Foo.
Try to add the following code to the tsconfig.json:
{
"compilerOptions": {
"moduleResolution": "NodeNext"
}
}
And then you can import the module like this:
import { Foo } from '#me/core/nodejs';
If you Don't Want to Touch the tsconfig.json you can use the following for the import:
import { Foo } from '#me/core/dist/nodejs';
References
Package entry points
Support for NodeJS 12.7+ package exports
A Proposal For Module Resolution

nodejs import module is not working as expecting,

I have the following structure.
node_modules
src
- app.js
- newrelic.js
package.json
package-lock.json
In newrelic.js its a simple class
const axios = require('axios')
export default class Newrelic {
static getName() {
return 'Hello;
}
}
in app.js
import Newrelic from "./newrelic";
console.log(Newrelic.getName())
When I run node src/app.js Sadly I receive
import Newrelic from "./newrelic";
^^^^^^^
SyntaxError: Cannot use import statement outside a module
What do I need to make this work?
In package.json add the field "type": "module" and your code will work.
More info here:
https://nodejs.org/api/esm.html
It is a very frequent question, when you deal with old packages. For example, it works with 'express-handlebars' v.6.x too.
Adding to file package.json the field "type": "module" resolves a similar conflict.

Sharing code between Firebase Functions and React

I'm using Firebase functions with a React application. I have some non-trivial code that I don't want to duplicate, so I want to share it between the deployed functions and my React client. I've got this working locally in my React client (though I haven't tried deploying) - but I can't deploy my functions.
The first thing I tried was npm link. This worked locally, but the functions won't deploy (which makes sense, since this leaves no dependency in your package.json). Then I tried npm install ../shared/ - this looked promising because it did leave a dependency in package.json with a file: prefix - but Firebase still won't deploy with this (error below).
My project directory structure looks like this:
/ProjectDir
firebase.json
package.json (for the react app)
/src
* (react source files)
/functions
package.json (for firebase functions)
index.js
/shared
package.json (for the shared module)
index.js
My shared module package.json (extraneous details omitted):
{
"name": "myshared",
"scripts": {
},
"dependencies": {
},
"devDependencies": {
},
"engines": {
"node": "8"
},
"private": true,
"version": "0.0.1"
}
My firebase functions package.json (extraneous details omitted):
{
"name": "functions",
"scripts": {
},
"dependencies": {
"myshared": "file:../shared",
},
"devDependencies": {
},
"engines": {
"node": "8"
},
"private": true
}
When I try to deploy with:
firebase deploy --only functions
It's telling me it can't load the module:
Function failed on loading user code. Error message: Code in file index.js can't be loaded.
Did you list all required modules in the package.json dependencies?
And I don't think the issue is how I export/imported my code- but just in case:
The export:
exports.myFunc = () => { some code };
The import (functions/index.js)
const myFunc = require('myshared');
And in my react code:
import { myFunc } from 'myshared';
So far the searching I've done hasn't yielded anything that works. Someone did mention entering the shared module path in firebase.json, but I couldn't find any details (including in the firebase docs) that show what that would look like. Thanks for any tips to get this going.
I found a solution. I'm not sure if it's the only or even the best solution, but it seems to work for this scenario, and is easy. As Doug noted above, Firebase doesn't want to upload anything not in the functions directory. The solution was to simply make my shared module a subdirectory under functions (ie ./functions/shared/index.js). I can then import into my functions like a normal js file. However, my shared folder also has a package.json, for use as a dependency to the react app. I install it using:
npm install ./functions/shared
This creates a dependency in my react app, which seems to resolve correctly. I've created a production build without errors. I haven't deployed the react app yet, but I don't think this would be an issue.
Another solution is to create a symlink. In terminal, under /ProjectDir, execute:
ln -s shared functions/shared
cd functions
npm i ./shared

Resources