I setup a mono repo using Ionic and Vue 3 the only issue is that in my child apps I still need to add the following into package.json. How can I use the root devDepenencies?
"devDependencies": {
"#vue/cli-plugin-typescript": "~5.0.0-rc.1"
},
Here is my structure:
apps
- app1
- package.json <-- here i have to add devDependency
- tsconfig.json
- app2
- package.json <-- here i have to add devDependency
- tsconfig.json
package.json <-- devDependency is in here too
tsconfig-base.json
Everything works this way but it creates a node_modules folder with a ton of dependencies in each child app. I'd like to avoid that if possible.
If I remove the devDependency I get the following error:
Module not found: Error: Can't resolve './src/main.js'
So its not seeing the dependency and thinking I'm using js but in fact i'm using typescript.
I tried setting the baseUrl in the tsconfigs but nothing has worked yet. Any help would be greatly appreciated.
Instead of configuring vue to find it you could set up a path link:
"devDependencies": {
"#vue/cli-plugin-typescript": "link:../node_modules/#vue/cli-plugin-typescript"
}
This would likely be your solution if you're using a monorepo. Add the following to your app1 and app2 package.json:
{
"vuePlugins": {
"resolveFrom": "insert path to parent package.json here"
}
}
See documentation here: https://cli.vuejs.org/guide/plugins-and-presets.html#installing-plugins-in-an-existing-project
I try to publish my angular library on npm.
But i get error
npm ERR! This package has been marked as private. Remove the 'private' field from the package.json to publish it.
But i don't have any private field in my package.json file
My package.json file of my library looks like this:
{
"name": "#myfirst_mylastname/testlib123",
"version": "0.0.1",
"peerDependencies": {
"#angular/common": "^11.1.2",
"#angular/core": "^11.1.2"
},
"dependencies": {
"tslib": "^2.0.0"
},
"description": "Test library",
"author": {"name":"test name"},
"keywords": ["test library"],
}
what i tried
I thought that private is always by default and i added
"private":"false" to my package.json file but i get same error on npm publish.
I googled and i found answers where they say - make private field true or false in the package.json file. I tried that but without success
Why is that ? How can be solved ?
In case this is a problem for anyone else, the error thrown by NPM when publishing a scoped package is misleading.
There may be no "private" field in your package.json file, however scoped packages have their access property set to restricted by default, whereas normal packages are public by default (https://docs.npmjs.com/cli/v8/commands/npm-publish#access).
To be able to publish your scoped package, ensure first that your terminal is in the root of the package - the publish command only affects the directory that you are in.
Then use a flag to set its access property when publishing:
npm publish --access=public
You should also be able to set the default access value for your package using the command
npm access public #myfirst_mylastname/testlib123
which would allow you to publish without specifying the flag. (https://docs.npmjs.com/cli/v8/commands/npm-access#details)
TL;DR How can I create an alias for a local yarn workspace dependency?
I've tried yarn workspaces before and never succeeded, and I'm giving it another try.
I've set "workspaces": ["packages/*"] in package.json.
For each package, I decided to use the naming convention #-/package-name to prevent naming conflicts and without worrying about having namespaces for internal packages.
When adding packages as dependencies, I've been following a style where I use an interface name for resolution, but point that towards a concrete implementation. This is what I did before using yarn workspaces:
"dependencies": {
"my-interface-name": "file:some/path/to/packages/some-concrete-implementation"
}
This is basically to allow what I like to call compile-time static dependency injection. And it also means each package can individually name their interface dependencies appropriately to their need and to prevent naming conflicts.
However, I can't figure out how to accomplish this with yarn workspaces. How do I create an alias for my yarn workspaces package #-/some-concrete-implementation called my-interface-name?
What I've already tried with no success:
Defining the dependency like "my-interface-name": "#-/some-concrete-implementation"} - for some reason this causes yarn to look for #-/some-concrete-implementation on the npm registry instead of in the local workspace
I've also tried to use the workspace protocol: "my-interface-name": "workspace:#-/some-concrete-implementation"} but it still looks for the package on the npm registry!
What I haven't yet tried and could work but removes benefits of using yarn workspaces in the first place:
"dependencies": {"my-interface-name": "file:../../node_modules/#-/some-concrete-implementation"}"
Have you seen the resolutions package.json key? Is it what you need?
I've used it for aliasing/overriding external packages but the example in the docs shows it working with local packages.
Resolutions
Allows you to override a version of a particular nested dependency. See the Selective Versions Resolutions RFC for the full spec.
{
"resolutions": {
"transitive-package-1": "0.0.29",
"transitive-package-2": "file:./local-forks/transitive-package-2",
"dependencies-package-1/transitive-package-3": "^2.1.1"
}
}
From the RFC:
"**/a" denotes all the nested dependencies a of the project.
"a" is an alias for **/a (for retro-compatibility, see below, and because if it wasn't such an alias, it wouldn't mean anything as it would represent one of the non-nested project dependencies, which can't be overridden as explained below).
So, I believe the rule you need is:
"**/my-interface-name": "file:some/path/to/packages/some-concrete-implementation"
// OR equivalent
"my-interface-name": "file:some/path/to/packages/some-concrete-implementation"
I believe it works in the package's package.json. Worst case you can hoist it to the workspace root and make the rule specific to the workspace e.g. "a/b".
The workspace: alias protocol (available in pnpm too) seems the direction to take.
I've also tried to use the workspace protocol: "my-interface-name": "workspace:#-/some-concrete-implementation"} but it still looks for the package on the npm registry!
Be sure to have yarn 3 installed, otherwise you'll run into weird issues.
Note that the syntax of "my-interface-name": "workspace:#-/some-concrete-implementation" looks incorrect.
It should be "#xxx/some-concrete-implementation": "workspace:*", assuming the name of linked the package is "name": "#xxx/some-concrete-implementation".
With this in mind you don't even need to create a specific #-/name. With workspace protocol, yarn will ensure it's never downloaded from npm. It becomes an internal workspace dependency.
PS:
Yarn 3 installation
Generally a simple yarn set version 3.0.2 && yarn plugin import workspace-tools) will work.
To avoid pnp current limitation, check the generated config .yarnrc.yml and ensure nmLinker is set to 'node-modules'
# Yarn 2+ supports pnp or regular node_modules installs. Use node-modules one.
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/#yarnpkg/plugin-workspace-tools.cjs
spec: "#yarnpkg/plugin-workspace-tools"
yarnPath: .yarn/releases/yarn-3.0.2.cjs
PS: you might want to add this to .gitignore too
.yarn/*
!.yarn/patches
!.yarn/releases
!.yarn/plugins
!.yarn/sdks
!.yarn/versions
.pnp.*
Run a yarn install just after.
About package.json's
Like you did, the root package.json will define the workspace paths:
{
"name": "monorepo",
"workspaces": [
"packages/*" // Enable package discovery in packages/* directory.
],
// ...
"devDependencies": {
"husky": "7.0.2", // Only what's needed for monorepo management
}
In your app packages/app/package.json
{
"name": "my-app",
"devDependencies": {
"#types/node": "16.10.1",
//...
},
"dependencies": {
// Assuming the name of packages/shared is "#your-org/some-concrete-implementation",
// we explicitly declare the dependency on it through
// workspace: alias (package-manager perspective)
"#your-org/some-concrete-implementation": "workspace:*",
}
}
You consumed package should declare the same name
{
"name": "#your-org/some-concrete-implementation",
}
Bonus: Typescript aliases
If your project is written in ts, you can even replicate your paths through
typescript path mapping. It will allow to include the files just as is (no prior compilation needed).
Following your example, just edit a ./packages/xxx/tsconfig.json in this way
{
"compilerOptions": {
// here baseUrl is set at ./src (good practice), can
// be set to '.'
"baseUrl": "./src",
"paths": {
// Declare deps here (keep them in sync with what
// you defined in the package.json)
// PS: path are relative to baseUrl
"#your-org/some-concrete-implementation/*": ["../../some-concrete-implementation/src/*"],
// if you have a barrel in ui-lib
"#your-org/some-concrete-implementation": ["../../some-concrete-implementation/src/index"],
}
},
}
PS: for non typescript: babel/plugin-module-resolver can be used in a similar manner.
I want to create a common package to share common login in convector chain code...this is useful has the project grows and we want to share common blocks of code, utils and other useful code to don't dry the project, or hard-code strings
but how can we do it if convector currently don't support it?
Currently convector can't use common vanilla packages (yet), only controllers,
to work we need to hack some stuff, like create a package that implement a controller, like a fake controller. and follow some steps
thanks to Diego and Walter of Covalent and a few hours
this post belongs to a github project that have a nest.js server, but currently is not created, when I have the link I update this post
first we start to create a lerna package for typescript, by hand
Create lerna common package #convector-rest-sample/common
packages/common/tsconfig.json
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "."
},
"include": [
"./src/**/*"
]
}
packages/common/package.json
{
"name": "#convector-rest-sample/common",
"version": "0.1.0",
"main": "dist/src/index",
"types": "dist/src/index",
"files": [
"dist"
],
"scripts": {
"build": "npm run clean && tsc",
"clean": "rm -rf ./dist",
"prepublishOnly": "npm run build"
},
"devDependencies": {
"typescript": "3.4.3"
}
}
Note: the paths "main": "dist/src/index" and "types": "dist/src/index" are important and must point to location of the index file, if we use a wrong path, we get errors using the package
create the index to export package files
packages/common/src/index.ts
export * from './constants';
packages/common/src/constants.ts
// convector model
const CONVECTOR_MODEL_PATH_PREFIX: string = 'io.worldsibu.examples';
const CONVECTOR_MODEL_PATH_PARTICIPANT: string = `${CONVECTOR_MODEL_PATH_PREFIX}.participant`;
const CONVECTOR_MODEL_PATH_PERSON: string = `${CONVECTOR_MODEL_PATH_PREFIX}.person`;
const CONVECTOR_MODEL_PATH_ATTRIBUTE: string = `${CONVECTOR_MODEL_PATH_PREFIX}.attribute`;
const CONVECTOR_MODEL_PATH_X509IDENTITY: string = `${CONVECTOR_MODEL_PATH_PREFIX}.x509identity`;
export const appConstants = {
CONVECTOR_MODEL_PATH_PARTICIPANT,
CONVECTOR_MODEL_PATH_PERSON,
CONVECTOR_MODEL_PATH_ATTRIBUTE,
CONVECTOR_MODEL_PATH_X509IDENTITY,
};
now add the common package to all packages in monorepo
# add to all packages (without scope)
$ npx lerna add #convector-rest-sample/common#0.1.0
# to prevent some problems always use same version has in local package
# clean and bootstrap
$ npx lerna clean -y && npx lerna bootstrap
optional can use --scope to add only to desired packages
# add to all packages (with scope)
$ npx lerna add #convector-rest-sample/common#0.1.0 --scope server --no-bootstrap
$ npx lerna add #convector-rest-sample/common#0.1.0 --scope participant-cc --no-bootstrap
$ npx lerna add #convector-rest-sample/common#0.1.0 --scope person-cc --no-bootstrap
# clean and bootstrap
$ npx lerna clean -y && npx lerna bootstrap --hoist
now test #convector-rest-sample/common in server, add this lines to top of packages/server/src/app.ts
to confirm that everything is working has expected
Note: if don't have a server, skip this step right to Use common package inside ChainCode section
import { appConstants as c } from '#convector-rest-sample/common';
debugger;
Logger.log(JSON.stringify(c, undefined, 2));
now launch server with debugger, and inspect c object or view log result
$ npx lerna run start:debug --scope server --stream
if outputs appConstants we are ready to go, and common package works has expected
Use common package inside ChainCode
To use common package inside chaincode, is very tricky, and I lost a few hours to get it work, thanks to Walter and Diego from WorldSibu I get it.
The problem is that currently, in convector there is no easy way to use packages, that are not controllers, for this to work we must create a fake controller in #convector-rest-sample/common to put it to work
first install required controller dependency in our #convector-rest-sample/common, this is required ut use { Controller, Invokable }
# install dependency
$ npx lerna add #worldsibu/convector-core --scope #convector-rest-sample/common
next we must create a fake controller in packages/common/src/common.controller.ts
import { Controller, Invokable } from '#worldsibu/convector-core';
#Controller('common')
export class CommonController {
#Invokable()
public async greeting() {
return 'Hello from CommonController';
}
}
don't forget to add export * from './common.controller'; to packages/common/src/index.ts to export controller
export * from './constants';
export * from './common.controller';
after that we must change chaincode.config.json to add the fake controller, this is a hell of a hack, we use a fake controller to force the #convector-rest-sample/common to be copied inside chaincode-person dir, without this, the #convector-rest-sample/common is not copied and we have a broken chain code when we try deploy it with cc:start or cc:upgrade it always show the annoying error npm ERR! 404 Not Found: #convector-rest-sample/common#0.1.0
first change chaincode.config.json
Tip: if don't have it in project, copy org1.person.config.json to chaincode.config.json and move on....
in my case I have only the legacy files org1.participant.config.json, org1.person.config.json, org2.participant.config.json and org2.person.config.json
this files can be deleted (Diego tip)
ok let's change chaincode.config.json and add another controller above person-cc
"controllers": [
{
"name": "participant-cc",
"version": "file:./packages/participant-cc",
"controller": "ParticipantController"
},
{
"name": "person-cc",
"version": "file:./packages/person-cc",
"controller": "PersonController"
},
// BO : ADD THIS
{
"name": "#convector-rest-sample/common",
"version": "file:./packages/common",
"controller": "CommonController"
}
// EO : ADD THIS
],
Note: this is another clever tricky part, the name is the package name, like the one we use in imports, version is the path location inside of our build chaincode-person
before build chaincode we must change our models to use the new common constants from #convector-rest-sample/common ex c.CONVECTOR_MODEL_PATH_X509IDENTITY, currently this common package only use simple constants, to keep it simple, the point is created common logic for all the packages, rest-server, front-end, packages-cc, cli-tools, etc
packages/participant-cc/src/participant.model.ts
import { appConstants as c } from '#convector-rest-sample/common';
...
export class x509Identities extends ConvectorModel<x509Identities>{
#ReadOnly()
public readonly type = c.CONVECTOR_MODEL_PATH_X509IDENTITY;
...
export class Participant extends ConvectorModel<Participant> {
#ReadOnly()
public readonly type = c.CONVECTOR_MODEL_PATH_PARTICIPANT;
...
packages/person-cc/src/person.model.ts
import { appConstants as c } from '#convector-rest-sample/common';
...
export class Attribute extends ConvectorModel<Attribute>{
#ReadOnly()
#Required()
public readonly type = c.CONVECTOR_MODEL_PATH_ATTRIBUTE;
...
export class Person extends ConvectorModel<Person> {
#ReadOnly()
#Required()
public readonly type = 'io.worldsibu.example.person';
...
now we can cc:package the chaincode chaincode-person, this will package the chaincode with our #convector-rest-sample/common inside it with packages person-cc and participant-cc using our #convector-rest-sample/common constants
# first remove chaincode (optional)
$ rm chaincode-person -r
# now manually invoke package command
$ npm run cc:package -- person org1
after package our chaincode is advised to check if common package is copied to chaincode-person folder to the right path
$ ls -la chaincode-person/packages
chaincode-person/packages/#convector-rest-sample
chaincode-person/packages/participant-cc
chaincode-person/packages/person-cc
another good practice is check if inside chaincode-person folder, in file chaincode-person/package.json, if our #convector-rest-sample/common was added to dependencies, in above json block we can see cc:package script add line "#convector-rest-sample/common": "file:./package/#convector-rest-sample/common", this is created based on our changes in chaincode.config.json remember, when we add the fake controller
"dependencies": {
"#theledger/fabric-chaincode-utils": "^4.0.1",
"#worldsibu/convector-core": "^1.3.3",
"#worldsibu/convector-storage-stub": "^1.3.3",
"reflect-metadata": "^0.1.12",
"tslib": "^1.9.0",
"participant-cc": "file:./packages/participant-cc",
"person-cc": "file:./packages/person-cc",
// BO: magic line here
"#convector-rest-sample/common": "file:./packages/#convector-rest-sample/common"
// EO: magic line here
},
done now we can deploy our chaincode with cc:start or cc:upgrade
Tip: if is first time use cc:start, if not use cc:package
to check that everything is fine from start, we restart our hyperledger stack, and start from the beginning, warning it destroy all data, if don't want to destroy data don't fire npm run env:restart and use cc:upgrade, more above
# this recreate environment and destroy all data
$ npm run env:restart
$ npm run cc:start -- person
# seed ledger
$ npm run seed
# create couchdb views
$ ./views/install.sh
# invoke person_create
$ npx hurl invoke person person_create "{ \"id\": \"1-100-103\", \"firstname\": \"Pete\", \"lastname\": \"Doe\", \"username\": \"pete\", \"password\": \"12345678\", \"email\": \"pete.doe#example.com\"}" -u admin
# invoke some stuff (wait for first invoke finish)
$ npx hurl invoke person person_getAll
done, everything is working has expected and we have a #convector-rest-sample/common package implemented.
if we check couchdb 1-100-103 person, we can check that is using type "type": "io.worldsibu.examples.person" that comes from our constants in our #convector-rest-sample/common, proving that it gets its value from #convector-rest-sample/common, believe me, if it won't wont find #convector-rest-sample/common it crash.....simple
for future changes in chaincode, upgrade it with above command
# upgrade chaincode
$ npm run cc:upgrade -- person 1.1
we are done........
Use scripts to copy other files to chaincode
another thing that I tried to hack before find the solution, is using npm scripts but it won't work because we need the modified chaincode-person/package.json with "#convector-rest-sample/common": "file:./packages/#convector-rest-sample/common" in the dependencies, but I try it......
leave it here, maybe can be useful for other kind of stuff, like copy other type of stuff
{
"scripts": {
...
"cc:package": "f() { npm run lerna:build; chaincode-manager --update --config ./$2.$1.config.json --output ./chaincode-$1 package; npm run copy:indexes -- $1; npm run copy:package:common -- $1; }; f",
...
"copy:package:common": "f () { mkdir -p ./chaincode-$1/node_modules/#convector-rest-sample/; cp -r ./packages/common/ ./chaincode-$1/node_modules/#convector-rest-sample/; }; f"
...
note for npm run copy:package:common -- $1; in "cc:package", and cp -r ./packages/common/ ./chaincode-$1/node_modules/#convector-rest-sample/; }; in "copy:package:common", it works, but won't modify chaincode-person/package.json with lines
"dependencies": {
"#theledger/fabric-chaincode-utils": "^4.0.1",
"#worldsibu/convector-core": "^1.3.3",
"#worldsibu/convector-storage-stub": "^1.3.3",
"reflect-metadata": "^0.1.12",
"tslib": "^1.9.0",
"participant-cc": "file:./packages/participant-cc",
"person-cc": "file:./packages/person-cc",
// BO: magic line here
"#convector-rest-sample/common": "file:./packages/#convector-rest-sample/common"
// EO: magic line here
}
Clean Up
to finish we can remove the legacy files org1.participant.config.json org1.person.config.json org2.person.config.json org2.participant.config.json, now we use the config file chaincode.config.json (thanks for the tip Diego)
# remove legacy files
rm org1.participant.config.json org1.person.config.json org2.person.config.json org2.participant.config.json
Note: don't forget to update packages.json "cc:package" with new ./chaincode.config.json file
package.json
"cc:package": "f() { npm run lerna:build; chaincode-manager --update --config ./chaincode.config.json --output ./chaincode-$1 package; npm run copy:indexes -- $1; }; f",
to finish thanks to all the kindly people of Covalent
UPDATE: in a fresh project/clone if I try to lerna bootstrap I get the below error
$ lerna bootstrap
src/participant.controller.ts:1:35 - error TS2307: Cannot find module '#convector-sample/common'.
1 import { appConstants as c } from '#convector-sample/common';
this is because the package require build before it's used
# build common library: this is required on fresh clones before lerna boostrap
$ npx lerna run build --scope #convector-sample/common --stream
now it works
$ lerna bootstrap
...
lerna success Bootstrapped 6 packages
I am not sure weather it is possible or not.
Is it possible to prevent publish when npm publish ran directly and make it accessible only via scripts.
User must be denied when npm publish is executed directly. i.e. User mush be able to publish via any scripts or npm run <script>
or
is there a way to tell npm only to publish <folder>/ or to look for a tarball when published.
If I mark it private I won't be able to publish at all. My main intention was to prevent accidental publishes.
NPM team gave a simple workaround which is awsome.
package.json
{
"prepublishOnly": "node prepublish.js",
"release": "RELEASE_MODE=true npm publish"
}
prepublish.js
const RELEASE_MODE = !!(process.env.RELEASE_MODE)
if (!RELEASE_MODE) {
console.log('Run `npm run release` to publish the package')
process.exit(1) //which terminates the publish process
}
Mark the package as private:
If you set "private": true in your package.json, then npm will refuse
to publish it.
This is a way to prevent accidental publication of private
repositories. If you would like to ensure that a given package is only
ever published to a specific registry (for example, an internal
registry), then use the publishConfig dictionary described below to
override the registry config param at publish-time.
{
"name": "some",
"version": "1.0.0",
"private": true
}
If you are trying to force something to happen before publishing, leverage the prepublish or prepublishOnly npm-script.
Yes, we can restrict npm to prevent accidental publish by making private: true in package.json
You can have script for publish also
In your package.json
{
"scripts": {
"publish:mypackages": "npm publish folder1/file1.tgz --registry http://custom-registry..."
}
}
Now in cmd: npm run publish:mypackages
It publishes the given tarball to the registry you have given.