> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lodemc.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrating from main.js

> The old API, point by point, and what is missing

A legacy expansion is a `main.js` with `module.exports.metadata` and `init()`, using the global `api`. Those still load, marked **Legacy** on the Expansions page, but they are deprecated and the SDK is where new work goes.

## The shape

<CodeGroup>
  ```js Before theme={null}
  module.exports.metadata = {
    name: 'My Expansion',
    version: '1.0.0',
    description: '...',
    author: 'Developer',
    id: 'my_expansion',
    apiKey: 'API-Key-Goes-Here'
  }

  module.exports.init = function () {
    api.nexomaker.registerModularPage('counter', __dirname + '/pages/Counter.jsx')
    api.nexomaker.postSidebarIcon({ key: 'counter', button: 'Counter', route: '/counter', page: 'counter', icon })
  }
  ```

  ```ts After theme={null}
  import sdk from 'sdk'
  import Counter from './pages/Counter'

  sdk.pages.register('counter', Counter)
  sdk.sidebar.add({ id: 'counter', page: 'counter', label: 'Counter', icon: 'Star' })
  ```
</CodeGroup>

```
MyExpansion/
├── meta.json      the old metadata, plus sdkVersion and permissions
├── config.json    { "start": "src/index.ts" }
└── src/
    ├── index.ts   what init() did, at the top level
    └── pages/Counter.tsx
```

Steps:

1. Move `metadata` into `meta.json`. Add `sdkVersion` (`^2.0.0`) and `permissions` (see below). Add `config.json`.
2. Replace `init()` with top-level code in the start file, or a [class](/writing-style#the-start-file) if you prefer.
3. Replace path strings with imports: `__dirname + '/pages/X.jsx'` becomes `import X from './pages/X'`.
4. In components, replace the props `api`, `useComponents()` and `useProjectState` with imports from `sdk` and `sdk/ui`.
5. Delete `api.expansion.call(...)`: logic and UI are one module graph now, so just import the function.

## Point by point

| Legacy                                                    | SDK                                                 |
| --------------------------------------------------------- | --------------------------------------------------- |
| `registerModularPage`, `regRoute`                         | `sdk.pages.register`                                |
| `postSidebarIcon`                                         | `sdk.sidebar.add`                                   |
| `registerInjection` / `unregisterInjection`               | `sdk.injections.add`, which returns the remover     |
| `registerEditorType`, `setEditorForType`                  | `sdk.editors.register`, `sdk.editors.forType`       |
| `registerModularCreator`                                  | `sdk.creators.register`                             |
| `registerItemType`                                        | `sdk.items.registerType`                            |
| `postEditorModule`                                        | `sdk.modules.register`                              |
| `postAppSettingsTab`, `postProjectSettingsTab`            | `sdk.settings.appTab`, `sdk.settings.projectTab`    |
| `defineThemeVariables`, `visuals.applyStyle`              | `sdk.theme.setVariables`, `sdk.theme.addStyle`      |
| `postLanguage`                                            | `sdk.i18n.addLanguage`                              |
| `listenEvent`, `emitEvent`                                | `sdk.events.on` (many per event), `sdk.events.emit` |
| `getProjects`, `project.loadNexoItems`, `yaml.read/write` | `sdk.project.*`, with permissions                   |
| `api.console.*`                                           | `sdk.log.*` or plain `console.*`                    |
| `api.expansion.call(fn)`                                  | an ordinary import                                  |
| `loadAsset`, `loadModel`, `imgconvert`                    | `import logo from './logo.png'`                     |
| `playSound(path)`                                         | `new Audio(importedUrl).play()`                     |

## Permissions

Legacy expansions had none: `api` could reach the whole project and the filesystem. In the SDK, declare what you use:

* reading items or project files: `project:read`
* writing them: `project:write`
* `fetch`: `network`
* clipboard: `clipboard`

## Not available yet

These have no SDK point, so an expansion that depends on them stays legacy for now:

* **Export formats:** `postExportFormat`, `registerExportFormat`, `extendExistingFormat`, `registerExporter`
* **Server reload commands:** `postReloadCommand`
* `postTemplate`, `postBlueprintCategory`, `postBlueprintPiece`
* `postAssets`, `postEditorModuleOverrides`, `postItemTypeStates`, `postCreatorTypeCompatibilities`, `postSettingsElementType`
* `execBlockbench`, and `loadFile` on paths outside the expansion
* `registerBackgroundModule`; background scripts (`sdk.run`) are planned

## Things that change quietly

* **Ids are namespaced.** A page registered as `counter` is `my_expansion.counter`, and its route changes to match. Anything you hard-coded needs updating.
* **Imports are real.** Helpers, shared files and CSS work now, which is usually a chance to delete the workarounds the old modulars needed.
* **Cleanup is automatic.** Anything you registered is removed on reload or disable; remove hand-rolled teardown.
* **No Node.** Any `require('fs')` or child process work has no equivalent and must go through the SDK, or stay legacy.
