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.
Related
I've a monorepo using nx with multiple node/nestjs apps. Some of the apps doesn't require all the packages used in the other apps. Because it's a monorepo, I need to install all packages for every apps during the deployment.
Is there a way generate a package.json on build that would contain only the packages needed for the app that I'm building?
I've tryed to use "generate-package-json-webpack-plugin" to generate the package.json, but it only detect half the dependencies.
I've also tried to build a single js file containing all the apps, but it doesn't seem to work and always require tslib.
After I look at the nx source code I found the answer.
Set generatePackageJson to true in workspace.json where <project-name>/targets/build/options.
This will generate you package.json with the necessary dependencies for your app.
Here example:
"node-api": {
"root": "apps/node-api",
"sourceRoot": "apps/node-api/src",
"projectType": "application",
"prefix": "node-api",
"targets": {
"build": {
"executor": "#nrwl/node:build",
"outputs": ["{options.outputPath}"],
"options": {
"showCircularDependencies": false,
"outputPath": "dist/apps/node-api",
"main": "apps/node-api/src/main.ts",
"tsConfig": "apps/node-api/tsconfig.app.json",
"assets": ["apps/node-api/src/assets"],
"generatePackageJson": true <----------------------
},
....
Nx encourages a single-version policy and has a single package.json.
If the issue is that you are installing all the dependencies every time in CI before building then you might need to rely on the functionality provided by your CI system to cache these between runs - a lot of existing CI systems do provide these:
* Gitlab: https://docs.gitlab.com/ee/ci/caching/
* CircleCI: https://circleci.com/docs/2.0/caching/
* Travis: https://docs.travis-ci.com/user/caching/
However this comes with its own set of issues (e.g. parallel jobs where one or more is changing the dependencies).
We can try to explore having a command in Nx: sort of a "affected:dep-install" that will detect which packages to install as part of the affected command. Please create an issue for it here: https://github.com/nrwl/nx/issues
In my reactjs project I have a dependency on a module that is in another gitrepo(stash) so I created this in my package.json as a property of dependencies :
"somemodule":{
"name": "somemodule",
"dependencies": {
"private-repo": "https://link_to_the_other_repo"
}
}
This repo contains a minified version and non minified version of the same file. When I run npm install the module does not get installed, there is no error message either. How can I create this dependency?
I have managed to get the dist/minified version of the file from the other repo. How can I refer to this in my package.json?
To add a private git module:
If you have RSA key added to your machine(secure & recommended):
"dependencies": {
"package-name": "git+ssh://git#github.com/project/repo.git"
}
If you want https:
"dependencies": {
"package-name": "git+https://username:password#github.com/project/repo.git"
}
Its not recommended to store username, password in your package.json file, so better add ssh keygen to your machine and use the first one.
I'm trying to use the linkurious library (a sigma fork), which provides a "main": "dist/sigma.require.js" (in the package.json). this allows me to do:
var sigma = require('linkurious');
however, the plugins are not included so I have to require them separately. the problem is that the plugins rely on the sigma variable being available in the global scope. so I've shimmed things as follows (from the package.json):
"browser": {
"sigma": "./node_modules/linkurious/dist/sigma.js",
"linkurious/plugins": "./node_modules/linkurious/dist/plugins.js"
},
"browserify-shim": {
"sigma": {"exports": "sigma"},
"linkurious/plugins": { "depends": [ "sigma" ] }
},
"browserify": {
"transform": [ "browserify-shim" ]
},
which, when run in a browser doesn't generate errors during inclusion of the plugins (I gather this means the global variable is available) but references to the plugins fail (as if they failed to attach themselves, or attached themselves to a non-global variable).
I'm using grunt-browserify to run the process where I have it configured like this (from the Gruntfile.js):
grunt.initConfig({
browserify: {
libs: {
files: { 'inc.js': ['index.js'] },
},
}
});
I've attached a little project to this issue with the minimal required code to demonstrate the problem in the hopes that someone else can replicate/figure out. unpack, type npm install; npm start and run a browser against http://localhost:8002/ to see the issue.
thanks in advance,
ekkis
sigma.zip
- edit I -
incidentally, bendrucker at the git repo (see: https://github.com/thlorenz/browserify-shim/issues/215) suggests I need to do a global transform. It's been explained to me that shimming doesn't work on node_modules files and for those I need a global transform. this doesn't make much sense to me as the whole point of shimming is that you don't own the code you're shimming. in any case, bendrucker pointed me to this other SO post where the question is posed but no answers are provided.
help?
I need to include a library that is present on github, but is not well-packaged; using Duo.js
At the moment of writing I am using the following to achieve what I desire:
bower
gulp
main-bower-files
Bower just downloades the library.
Gulp, with main-bower-files are useful to override the single package options and setup a so-called "main file" that I can build.
Example:
gulp.task('copy-libs', function () {
return gulp.src(bowerFiles({ env: 'development' }))
.pipe(gulp.dest('build/libs/'));
});
bower.json file:
"dependencies": {
"cash": "sudo-js/cash",
"bootstrap": "~3.3.2",
"delorean": "~0.8.7",
"react": "~0.12.2"
},
"overrides": {
"cash": {
"main": {
"development": "build/debug/cash.js"
}
}
}
}
How can i achieve this with duojs?
The documentation is quite thin regarding libraries that does not ship with a valid component.json
You can specify the path to an entry file for your lib. It won't be as clean as just specifying user/repo, but it'll get the job done.
For example, when including Twitter Bootstrap from twbs/bootstrap
require('twbs/bootstrap#v3.3.2:dist/js/bootstrap.js');
// repo: twbs/bootstrap
// version/tag: v3.3.2
// path: dist/js/bootstrap.js
Unfortunately, this doesn't work out-of-the-box since it assumes you have the jQuery global... so you need to add this above the previous line.
jQuery = require('components/jquery'); // leave out `var` so it becomes a global
This includes jQuery from the wonderful components project. (they package up popular libs so they can be consumed by various package managers.
Also, it turns out there is a components/bootstrap that is properly packaged with a component.json.
So, you can actually make bootstrap work with the following:
jQuery = require('components/jquery');
require('components/bootstrap');
For the other libraries that aren't as common, you can use the process mentioned first to specify the path to the right JS/CSS file. (ie: user/repo#version:path)
I'm trying to install behat + mink (on kohana framework, not symfony, I'm putting behat into the modules folder - I'm writting this just in case, but I guess that's not what causes my problem).
I am having the same search.feature file as it is in behat documentation, I'm changing features/bootstrap class into the:
<?php
use Behat\Behat\Context\ClosuredContextInterface,
Behat\Behat\Context\TranslatedContextInterface,
Behat\Behat\Context\BehatContext,
Behat\Behat\Exception\PendingException;
use Behat\Gherkin\Node\PyStringNode,
Behat\Gherkin\Node\TableNode;
use Behat\MinkExtension\Context\MinkContext;
/**
* Features context.
*/
class FeatureContext extends MinkContext
{
}
And when I type "behat" in the CLI I get the following error: Call to a member function getSession() on a non object in .....RawMinkContext.php on line 80.
I've read somewhere that it's the behat.yml file which causes this error. I think the right thing to do is to create new behat.yml file in the root of the installed behat folder and put this code inside:
default:
extensions:
Behat\MinkExtension\Extension:
base_url: http://wikipedia.org
goutte: ~
selenium2: ~
paths:
features: features
bootstrap: features/bootstrap
annotations:
paths:
features: features/annotations
closures:
paths:
features: features/closures
But of couse it gives me the same error. I've tried a lot of configurations: copying only extensions part, changing default into context, copying the same content into three other behat.yml files (vendor/behat/behat, and vendor/behat/monk, and vendor/behat/monk-ententions) - none works.
Can someone tell me what's the right way to set this? Maybe someone here also had problems with that...
BTW. When I installed behat withough goutte, only with selenium2 driver, I was getting errors that goutte is not installed. But when I installed it with goutte, I was getting errors that there's no fabpot/goutte directory (or fapbot/, I don't remember, but I guess it was the first one :D), so I deleted everything and reinstalled behat with the following composer.json file, maybe this also has something to do with this error:
{
"name": "behat/mink-browserkit-driver",
"description": "Symfony2 BrowserKit driver for Mink framework",
"keywords": ["Symfony2", "testing", "browser"],
"homepage": "http://mink.behat.org/",
"type": "mink-driver",
"license": "MIT",
"authors": [
{
"name": "Konstantin Kudryashov",
"email": "ever.zet#gmail.com",
"homepage": "http://everzet.com"
}
],
"require": {
"php": ">=5.3.1",
"symfony/browser-kit": ">=2.0.0,<2.2.0",
"symfony/dom-crawler": ">=2.0.0,<2.2.0",
"behat/behat": "2.4.*#stable",
"behat/mink": "1.4.*#stable",
"behat/mink-extension": "*",
"behat/mink-goutte-driver": "*",
"behat/mink-selenium2-driver": "*"
},
"minimum-stability": "dev",
"autoload": {
"psr-0": {
"Behat\\Mink\\Driver": "src/"
}
},
"config": {
"bin-dir": "bin/"
}
}
The recommended way to install Behat+Mink for anything, not just Kohana, is via Composer. See http://docs.behat.org/quick_intro.html for instructions. The autoload clause you have in your composer.json is not required.
As for your modules attempt, KO3.2 does not yet have the ability to load PSR-0 and so putting it in modules will not allow it to be loaded correctly.
Don't randomly copy your behat.yml - you only need one copy in your project root. If you are worried, you can explicitly load your behat.yml via bin/behat -c /path/to/behat.yml
With your composer.json set up correctly to have mink dependencies, you can then just do bin/behat -dl to verify mink works.
You can then do bin/behat --init to create your features filestructure.
This will create a FeatureContext which overrides Mink's definitions, so add
require_once __DIR__.'/../../vendor/autoload.php';
and change the class definition to:
class FeatureContext extends Behat\MinkExtension\Context\MinkContext
in your features/bootstrap/FeatureContext.php file.
Everything should work as expected now. I recently did a Behat+Mink+KO3 setup, feel free to see how I did it here: https://github.com/Moult/Eadrax/commit/b5dd813c92b82aea29eea13b5a30bae170aa57e6