Skip to content

The Good Ole Registry (Lego Basket)

Let's not keep our toys everywhere, let's be good citizens and put them in a basket.

PSS!!! Come here lemme tell you a secret - are you a frontend developer? "The DOM is not your enemy" we talk about Light DOM and Shadow DOM in a minute 😉.

Topic 2: The Registry & Internal Storage

LegoDOM uses three specialized collections to store the "DNA" of your application. This separation allows the framework to distinguish between what a block looks like versus how it behaves.

js
const registry = {};
const legoFileLogic = new Map();
const sharedStates = new Map();

1. The HTML Blueprint Collection (registry)

const registry = {} is a plain object that stores references to <template> elements.

  • When LegoDOM initializes, it scans the DOM for any <template b-id="my-block"> and saves it here.

  • The b-id becomes the key, and the DOM node itself is the value.

  • Purpose: This is the "Light DOM" source used to populate the "Shadow DOM" later during the "snapping" process.

2. The Lego File Logic Collection (legoFileLogic)

const legoFileLogic = new Map(); stores the JavaScript objects passed into Lego.block().

  • While registry holds the HTML/CSS, legoFileLogic holds the functions like mounted(), updated(), or custom methods.

  • Why a Map? Unlike a plain object, a Map is more performant for frequent lookups by string keys (tag names) - I think!!! or maybe I read wrong (call me out).

3. The Singleton States Collection (sharedStates)

const sharedStates = new Map(); is one of the most powerful "hidden" features of LegoDOM.

  • Every time you define a block, Lego creates a reactive version of its logic and stores it here.

  • This allows other blocks to access a specific block's state globally via the $registry('tag-name') helper. NOTE the block's (template/blueprint) state, not the instance state.

  • Example: If you have 3 user-profile blocks, any other block on the page can peek at their (shared) state data by asking the sharedStates map.

Dissecting Registration

The registry doesn't just wait for you to type; it's fed by three distinct streams across 2 paradigms.

Paradigm 1: Explicitly

js
const legoFileLogic = new Map(); // Specifically for SFB script logic
// ...
block: (tagName, templateHTML, logic = {}) => {
  const t = document.createElement('template');
  // ... stores template in registry
  legoFileLogic.set(tagName, logic); // Stores the JS part separately
}

Paradigm 2: Implicitly

js
// vite-plugin.js
async buildStart() {
  const root = config?.root || process.cwd();
  legoFiles = await fg(include, { cwd: searchPath, absolute: true }); // Scans for .lego files
}
// ...
async load(id) {
  if (id.endsWith('.lego')) {
    const blockCall = generateBlockCall(parsed); // Converts .lego file to Lego.block()
    return `import { Lego } from 'lego-dom/main.js';\n${blockCall}`;
  }
}

The Three Ways to Register:

  1. The HTML Manual Method: You put <template b-id="my-comp"> directly in your *.html files. During Lego.init(), LegoDOM scrapes these and populates the registry. This is great for "no-build" prototypes.

  2. The Lego.block() JS Method: You call Lego.block('my-comp', '<h1>Hi</h1>', { ... logic }) in a standard JavaScript file.

  3. The Lego File Automatic Method (The "Vite Way"): * The Vite Plugin (as seen in vite-plugin.js) acts as a build-time robot.

    • It uses fast-glob (fg) to scan your entire src/blocks directory for any file ending in .lego.

    • It parses the <template> and <script> inside that .lego file.

    • It injects a Lego.block() call into your JavaScript bundle automatically.

    • Result: You just create a file named user-card.lego, and suddenly <user-card> is a valid HTML tag in your app.

Paradigm 3: Runtime Block Definition (New in v2.0)

js
defineLegoFile: (content, filename) => {
  // Regex parsing of <template>, <script>, <style>
  const templateMatch = content.match(/<template([\s\S]*?)>([\s\S]*?)<\/template>/);
  // ...
  const logicObj = new Function(`return ${script}`)();
  // ...
  legoFileLogic.set(name, logicObj);
}

This is the power behind the Server Loader. We can fetch a string from the server and "compile" it in the browser using new Function(). It populates registry and legoFileLogic just like the build-time tools, but on the fly.

4. Block Naming Conventions ("Explicit or Go Home")

When you define a block via a file (e.g. .lego), LegoDOM automatically derives the tag name. To keep the web platform happy, we enforce Custom Element Best Practices:

  1. Automatic Conversion: All filenames are converted to kebab-case.

    • UserProfile.lego -> <user-profile>
    • navBar.lego -> <nav-bar>
    • data_table.lego -> <data-table>
  2. The Hyphen Rule: A custom element MUST contain a hyphen.

    • Button.lego -> Error
    • Adidas.lego -> Error

TIP

Single Word Block? Namespace It! Custom Element specs require a hyphen to distinguish from native tags. To ensure forward compatibility, LegoDOM will throw an error if you try to define a single-word block. Simply add your app or product prefix to the filename:

  • fb-button.lego -> <fb-button>
  • shop-card.lego -> <shop-card>
  • mobile-button.lego -> <mobile-button>

Released under the MIT License.