skip to Main Content

If you download Firebase JS SDK you only need to add one dependency:
firebase

From there you can access all different modules based on your import attributes:

import { initializeApp } from 'firebase/app';
import { getFirestore, collection, getDocs } from 'firebase/firestore/lite';

And then any library that won’t be used is automatically removed if you use any tree-shaking tool.

How can I achieve something similar?
I’m looking to build a collection of packages for our organization so that, instead of having everyone working and updating 15 packages, having a single package that can be divided into modules/imports so a project only imports the necessary modules.

I have been looking for some days and I don’t even know how to name this approach.

Could anyone link me to a tutorial, a document or anything to be able to start my investigation?

3

Answers


  1. There’s nothing particular to do : it’s just natural.

    For example, the following structure :

    foo
    ├── bar
    │   ├── baz
    │   │   └── index.js
    │   └── index.js
    ├── fum
    │   ├── index.js
    │   └── qux
    │       └── index.js
    ├── index.js
    └── package.json
    

    Allows the following usage :

    import foo from './foo';
    import foobar from './foo/bar';
    import foobarbaz from './foo/bar/baz';
    import foofum from './foo/fum';
    import foofumqux from './foo/fum/qux';
    

    For testing purposes, consider each js file exports a string containing the concatenated value of all its parent folder names, e.g. ./foo/bar/baz/index.js contains export default 'foobarbaz';, while package.json contains { "type": "module" }.

    Then, the following code :

    console.log({
        foo,
        foobar,
        foobarbaz,
        foofum,
        foofumqux
    });
    

    Outputs :

    {
      foo: 'foo',
      foobar: 'foobar',
      foobarbaz: 'foobarbaz',
      foofum: 'foofum',
      foofumqux: 'foofumqux'
    }
    
    Login or Signup to reply.
  2. If you check out the Firebase JS SDK repository, you can see that they use a monorepo to manage all of this, and they are actually all separate packages. They use Lerna to manage their monorepo – but there are several monorepo tools out there (npm workspaces, yarn). I like npm workspaces myself since it’s native.

    What they do is they make heavy use of exported modules in npm. You can see the documentation for that here: https://nodejs.org/api/packages.html#exports

    So they have a repository that looks vaguely like this:

    my-project
    ├── packages
    │   ├── pkg-1
    │   │   ├── package.json
    │   │   └── index.js
    │.  ├── pkg-2
    │   │   ├── package.json
    │   │   └── index.js
    │   └── all
    │.      ├── pkg-1
    │       │   └── index.js
    │.      ├── pkg-2
    │       │   └── index.js
    │       ├── package.json
    │       └── index.js
    └── package.json
    
    

    The key part of the root package.json is the declaration of packages:

    {
      ...
      packages: [
        "packages/*",
      ],
      ...
    }
    

    Sample package.json from a package:

    {
      "name": "@my-project/pkg-1",
      "main": "index.js",
      ...
    }
    

    The trick here is where they have an extra package which just exports all of the other packages in the repo:

    my-project/packages/all/package.json:

    {
      "name": "my-project"
      "exports": {
        "./pkg-1": "./pkg-1/index.js",
        "./pkg-2": "./pkg-2/index.js",
      }
      "dependencies": [
        "@my-project/pkg-1": "*",
        "@my-project/pkg-2": "*",
      ],
      ...
    }
    

    example /my-project/packages/all/pkg-1/index.js:

    export * from "@my-project/pkg-1"
    

    Now when someone tries to install your package all, they will get all of the other packages aliased.

    import * as pkg1 from "all/pkg-1"
    

    They also bundle together ES modules and CommonJS so you can use "import" or "require". Take a long hard look at their package.json files in their repository, it’s a work of art! https://github.com/firebase/firebase-js-sdk

    Login or Signup to reply.
  3. TLDR; In your package.json add the files property. This will expose the directories that you specify. This will also make them accessible by path as you seek.

    {
      "name": "mybase",
      "main": "index.js",
      "files": ["app/**", "store/**"]
    }
    

    With a config like above you would be able to access a file like this:

    import { initializeApp } from 'mybase/app';
    import { getFirestore, collection, getDocs } from 'mybase/store/lite';
    

    Just make sure that the files exist as @KaKi87 mentioned. Specifying the files property in your package.json is key to exposing your file structure to client apps.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search