2

Most of the Atlaskit editor is licensed with MIT or Apache 2.0 License. I'm trying to figure out how Atlaskit is supposed to be used without accepting non-free (as in freedom) Atlassian Design Guidelines License (ADG License) which is used for packages @atlaskit/icon, @atlaskit/icon-file-type and @atlaskit/icon-object which are part of deeply nested dependencies for the Atlaskit editor.

I'm fully aware that I need to re-create alternative icons and themes and I'm fine with that. However, when I create package.json and run npm install I get the above mentioned ADG licensed packages when I include following dependencies:

...
"dependencies": {
    "@atlaskit/editor-core": "^120",
    "@atlaskit/media-core": "^31",
    "@atlaskit/smart-card": "^13",
    ...

How can I pass my own implementation to be used e.g. instead of ADG licensed default @atlaskit/icon from npmjs.com?

Forking the whole Atlaskit package tree just to modify dependencies to override these 3 packages is obviously possible but is there a better way? It seems that npm-force-resolutions can do something like this but as far as I know, it only allows replacing the dependency version number so I could simply select which official implementation of e.g. @atlaskit/icon I would like to use but that doesn't allow changing to totally different implementation. All versions of @atlaskit/icon have identical license and I'm trying to do this because I don't like ADG license and I'm willing to reimplement the required parts.

I would prefer something that I can save in main level package.json (or in a file next to it) where I require other atlaskit dependencies. I'm using node v12 in case it makes a difference. I don't need to support any lesser version but I'd like to be compatible with v14, too.

Update: given above dependencies, npm list "@atlaskit/icon" will emit

my-atlaskit-editor@0.0.1 /local/path/to/my/editor
├─┬ @atlaskit/editor-core@120.1.2
│ ├─┬ @atlaskit/calendar@9.2.10
│ │ └── @atlaskit/icon@20.1.2  deduped
│ ├─┬ @atlaskit/checkbox@10.1.14
│ │ └── @atlaskit/icon@20.1.2  deduped
│ ├─┬ @atlaskit/datetime-picker@9.4.7
│ │ ├─┬ @atlaskit/field-base@14.0.5
│ │ │ └── @atlaskit/icon@20.1.2  deduped
│ │ └── @atlaskit/icon@20.1.2  deduped
│ ├─┬ @atlaskit/droplist@10.0.8
│ │ └── @atlaskit/icon@20.1.2  deduped
│ ├─┬ @atlaskit/editor-common@44.1.0
│ │ ├── @atlaskit/icon@20.1.2  deduped
│ │ ├─┬ @atlaskit/media-picker@54.2.3
│ │ │ ├─┬ @atlaskit/flag@12.4.5
│ │ │ │ └── @atlaskit/icon@20.1.2  deduped
│ │ │ ├── @atlaskit/icon@20.1.2  deduped
│ │ │ └─┬ @atlaskit/media-card@68.0.2
│ │ │   └── @atlaskit/icon@20.1.2  deduped
│ │ └─┬ @atlaskit/profilecard@12.4.4
│ │   ├─┬ @atlaskit/avatar@18.0.2
│ │   │ └── @atlaskit/icon@20.1.2  deduped
│ │   └── @atlaskit/icon@20.1.2  deduped
│ ├─┬ @atlaskit/editor-markdown-transformer@3.1.25
│ │ └─┬ @atlaskit/editor-common@46.1.1
│ │   ├── @atlaskit/icon@20.1.2  deduped
│ │   ├─┬ @atlaskit/media-card@68.0.2
│ │   │ └── @atlaskit/icon@20.1.2  deduped
│ │   └─┬ @atlaskit/media-picker@54.2.3
│ │     ├─┬ @atlaskit/flag@12.4.5
│ │     │ └── @atlaskit/icon@20.1.2  deduped
│ │     └── @atlaskit/icon@20.1.2  deduped
│ ├─┬ @atlaskit/emoji@62.8.4
│ │ └── @atlaskit/icon@20.1.2  deduped
│ ├─┬ @atlaskit/form@7.4.1
│ │ └── @atlaskit/icon@20.1.2  deduped
│ ├── @atlaskit/icon@20.1.2 
│ ├─┬ @atlaskit/media-card@67.2.3
│ │ ├── @atlaskit/icon@20.1.2  deduped
│ │ └─┬ @atlaskit/media-viewer@44.4.4
│ │   └── @atlaskit/icon@20.1.2  deduped
│ ├─┬ @atlaskit/media-editor@37.0.12
│ │ ├── @atlaskit/icon@20.1.2  deduped
│ │ └─┬ @atlaskit/media-card@68.0.2
│ │   └── @atlaskit/icon@20.1.2  deduped
│ ├─┬ @atlaskit/media-filmstrip@38.0.4
│ │ ├── @atlaskit/icon@20.1.2  deduped
│ │ └─┬ @atlaskit/media-card@68.0.2
│ │   └── @atlaskit/icon@20.1.2  deduped
│ ├─┬ @atlaskit/media-picker@54.2.3
│ │ ├─┬ @atlaskit/flag@12.4.5
│ │ │ └── @atlaskit/icon@20.1.2  deduped
│ │ ├── @atlaskit/icon@20.1.2  deduped
│ │ └─┬ @atlaskit/media-card@68.0.2
│ │   └── @atlaskit/icon@20.1.2  deduped
│ ├─┬ @atlaskit/mention@18.18.3
│ │ ├─┬ @atlaskit/avatar@18.0.2
│ │ │ └── @atlaskit/icon@20.1.2  deduped
│ │ └── @atlaskit/icon@20.1.2  deduped
│ ├─┬ @atlaskit/modal-dialog@10.6.4
│ │ └── @atlaskit/icon@20.1.2  deduped
│ ├─┬ @atlaskit/radio@3.2.3
│ │ └── @atlaskit/icon@20.1.2  deduped
│ ├─┬ @atlaskit/select@11.0.14
│ │ └── @atlaskit/icon@20.1.2  deduped
│ ├─┬ @atlaskit/status@0.9.25
│ │ └── @atlaskit/icon@20.1.2  deduped
│ └─┬ @atlaskit/task-decision@16.1.2
│   └── @atlaskit/icon@20.1.2  deduped
├─┬ @atlaskit/media-picker@54.2.3
│ ├─┬ @atlaskit/dropdown-menu@9.0.6
│ │ └── @atlaskit/icon@20.1.2  deduped
│ ├─┬ @atlaskit/flag@12.4.5
│ │ └── @atlaskit/icon@20.1.2  deduped
│ ├── @atlaskit/icon@20.1.2  deduped
│ ├─┬ @atlaskit/media-card@68.0.2
│ │ └── @atlaskit/icon@20.1.2  deduped
│ └─┬ @atlaskit/media-ui@12.5.1
│   ├─┬ @atlaskit/avatar@18.0.2
│   │ └── @atlaskit/icon@20.1.2  deduped
│   └── @atlaskit/icon@20.1.2  deduped
└─┬ @atlaskit/smart-card@13.5.1
  └── @atlaskit/icon@20.1.2  deduped

Is it possible to replace just package @atlaskit/icon without replacing all the packages in the tree between my-atlaskit-editor and @atlaskit/icon. In this example it would be following list:

@atlaskit/avatar
@atlaskit/calendar
@atlaskit/checkbox
@atlaskit/datetime-picker
@atlaskit/dropdown-menu
@atlaskit/droplist
@atlaskit/editor-common
@atlaskit/editor-core
@atlaskit/editor-markdown-transformer
@atlaskit/emoji
@atlaskit/field-base
@atlaskit/flag
@atlaskit/form
@atlaskit/media-card
@atlaskit/media-editor
@atlaskit/media-filmstrip
@atlaskit/media-picker
@atlaskit/media-ui
@atlaskit/media-viewer
@atlaskit/mention
@atlaskit/modal-dialog
@atlaskit/profilecard
@atlaskit/radio
@atlaskit/select
@atlaskit/smart-card
@atlaskit/status
@atlaskit/task-decision

That's lots of packages to override just to change the dependencies in nested packages. In practice, I would need to fork all those packages and change dependencies of each nested package to refer to my forked version which again has only modified dependencies pointing to yet another package forked by me. At the leaf of this whole replaced tree would be a package.json that points to free variants of @atlaskit/icon, @atlaskit/icon-file-type and @atlaskit/icon-object.

Is there really no way to fix this mess without forking and modifying dependencies in the whole tree?

Mikko Rantalainen
  • 14,132
  • 10
  • 74
  • 112

2 Answers2

3

NPM natively supports package aliases since version 6.9.0

So in your case you could take the following approach:

  "dependencies": {
    "@atlaskit/editor-core": "npm:another-editor-core@^1.0",
    "@atlaskit/media-core": "npm:another-media-core@^1.0",
    "@atlaskit/smart-card": "npm:another-smart-card@^1.0"
  }

This will have the effect of installing another-editor-core@^1.0 instead of @atlaskit/editor-core. Then when your code attempt to require @atlaskit/editor-core, another-editor-core will be required instead:

const editor = require("@atlaskit/editor-core") // this is actually another-editor-core

An easy way to manage that would be to create an org in npm so that you can have scoped packages like @my-org-name/editor-core

Edit:

As pointed in the comments, the solution above won't support transitive dependencies, but only direct dependencies.

It seems that this is currently not supported by npm even using npm-force-resolutions. After a bit of research I couldn't find any other tool that would make that work with npm.

However this is supported by yarn using selective dependency resolutions:

  "resolutions": {
    "@atlaskit/editor-core": "npm:another-editor-core@^1.0",
    "@atlaskit/media-core": "npm:another-media-core@^1.0",
    "@atlaskit/smart-card": "npm:another-smart-card@^1.0"
  }

So I see 3 options for you moving forward:

  1. Migrate to yarn (if your project allow this)
  2. Write a script that parse package-lock.json to override the resolved packages. That should not actually be that hard. The script could be run as a preinstall step, exactly like npm-force-resolutions
  3. Request this feature on npm-force-resolutions repo, possibly contributing to the repo yourself if familiar with clojure and time permit.

Good luck!

Anthony Garcia-Labiad
  • 3,531
  • 1
  • 26
  • 30
  • Yes, this explains how to override the toplevel packages. However, in case of Atlaskit editor code, the problem is nested packages deep in the dependency tree. How to override those nested dependencies without having to fork the whole tree? See the update in the question for details. – Mikko Rantalainen Oct 06 '20 at 13:10
  • According to this https://github.com/npm/rfcs/blob/latest/implemented/0001-package-aliases.md the npm aliases system doesn't support "transitive dependencies" as they call it when your dependency has an dependency by itself. – Mikko Rantalainen Oct 06 '20 at 13:25
  • 1
    You are correct. I edited my answer to add the 3 options I can see to handle transitive dependencies. There is also of course the option to fork atlaskit as you pointed out in the question description. – Anthony Garcia-Labiad Oct 06 '20 at 19:18
  • I think I'll try migrating the whole project to yarn. That doesn't seem really easy solution but might be easier in long run. – Mikko Rantalainen Oct 09 '20 at 09:40
0

An alternative would be to create a private NPM package using the @atlaskit package as a base, then replacing the dependencies that with your own packages. See https://docs.npmjs.com/files/package.json for details on creating a package description and https://docs.npmjs.com/creating-and-publishing-private-packages for publishing a private package. You may want to consider using a tool to automate the update process to pull the latest @atlaskit package and create and publish your alternative.

Xavier
  • 1,383
  • 7
  • 6
  • That would require replacing whole tree because some packages that would need to be fixed are nested very deeply. For example, @atlaskit/editor-core -> @atlaskit/editor-markdown-transformer -> @atlaskit/editor-common -> @atlaskit/media-picker -> @atlaskit/flag -> @atlaskit/icon. Obviously, I would rather not do this just to replace the icons. – Mikko Rantalainen Oct 09 '20 at 09:52