Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Typescript Bundle Laravel Package

sensiolabs/typescript-bundle

SensioLabs TypeScriptBundle integrates the TypeScript compiler into Symfony, helping you compile .ts assets to JavaScript and manage TypeScript sources alongside your application. It provides bundle configuration and tooling for a smoother TypeScript workflow.

View on GitHub
Deep Wiki
Context7

Getting Started

Prerequisites. This bundle is an adapter between Symfony's AssetMapper and an external TypeScript transpiler. You need:

  • symfony/asset-mapper (the bundle does nothing without it),
  • a transpiler binary on PATH — the bundle is built around Bun (single binary, native TS transpilation, no node_modules needed), with Node/tsc as the usual fallback,
  • a tsconfig.json at the project root for editor/type-checking support (the transpiler does not need it, but your IDE does).
composer require symfony/asset-mapper        # if not already installed
composer require sensiolabs/typescript-bundle

# transpiler — pick one and install it on every machine that builds assets
curl -fsSL https://bun.sh/install | bash
# or: npm i -g typescript

Symfony Flex registers the bundle automatically. If not, add it to config/bundles.php for the all environment.

Where to look first.

  1. The GitHub README for sensiolabs/AssetMapperTypeScriptBundle — it is the only authoritative source; the bundle is young and options have shifted between 0.x/1.x releases.
  2. php bin/console config:dump-reference typescript — prints the real config tree for the version you installed. Treat any example config (including the ones below) as a starting point and confirm the keys here.
  3. php bin/console list typescript — shows the commands the bundle actually registers (typically a build command and a watch/dev variant).
  4. php bin/console debug:container --tag=asset_mapper.compiler — confirms the TypeScript compiler is wired into AssetMapper's compilation pipeline.

First use case — a typed entry point.

// assets/app.ts
interface Greeting { name: string }
const greet = (g: Greeting): string => `Hello, ${g.name}`;

const el = document.querySelector<HTMLElement>('#app');
if (el) el.textContent = greet({ name: 'TypeScript' });

Compile it (command name depends on version — check list typescript):

php bin/console typescript:build

Then reference the compiled asset exactly as you would a normal AssetMapper asset:

{# templates/base.html.twig #}
{{ importmap('app') }}

Verify what AssetMapper sees before debugging anything else:

php bin/console debug:asset-map | grep app
php bin/console importmap:validate

If app.js (or app.ts) shows up with the expected logical path, the pipeline is wired correctly and any remaining problem is Twig-side.

Implementation Patterns

Build-time compilation, not request-time. The productive pattern is: .ts sources live under assets/, a build step emits .js, and AssetMapper serves the emitted .js. In development, run the bundle's watch command in a second terminal (or under a process manager) so saves are picked up without a manual rebuild. In production, run the build as part of the deploy — before php bin/console asset-map:compile, so the dump contains the compiled JavaScript and not the TypeScript sources.

# deploy pipeline
composer install --no-dev --optimize-autoloader
php bin/console typescript:build          # emit .js
php bin/console asset-map:compile         # dump into public/assets/
php bin/console cache:warmup

Transpile in the bundle, type-check in CI. Treat the bundle as a transpiler, not a checker. Transpilers strip types and never report type errors. Add a separate, explicit type-check step so red squiggles in the IDE are backed by CI:

// package.json
{
  "scripts": {
    "typecheck": "tsc --noEmit"
  },
  "devDependencies": {
    "typescript": "^5"
  }
}

Run npm run typecheck in CI and locally as a pre-commit hook. This is the single most important workflow habit with this bundle: without it, a typo in a type annotation ships silently.

Import maps for runtime, @types for the editor. AssetMapper resolves bare specifiers through the import map, not through node_modules. So:

php bin/console importmap:require @hotwired/stimulus
# editor-only types, not shipped
npm i -D @types/node @types/webpack-env

Then in TS you can import { Controller } from '@hotwired/stimulus' and both the browser and the type checker are satisfied. Keep these two dependency lists in sync — a very common failure is "works in editor, 404 in browser" because the import-map entry was never added.

Stimulus controllers. The most natural application for this bundle: type your Stimulus controllers.

// assets/controllers/hello_controller.ts
import { Controller } from '@hotwired/stimulus';

export default class extends Controller<HTMLElement> {
    static targets = ['output'];
    declare readonly outputTarget: HTMLElement;

    connect(): void {
        this.outputTarget.textContent = 'connected';
    }
}

Register the controller directory in assets/controllers.json exactly as you would for .js controllers; the bundle only changes how the source is compiled.

Per-page entry points. Rather than one monolithic app.ts, emit one entry per page (assets/pages/home.ts, assets/pages/checkout.ts) and reference the page-specific one in each template's importmap(). This keeps the import map small and maps cleanly onto Symfony's per-template asset conventions.

tsconfig.json that matches transpiler semantics. Because compilation is per-file, mirror that in your config so the editor and the transpiler agree:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "isolatedModules": true,
    "verbatimModuleSyntax": true,
    "noEmit": true
  },
  "include": ["assets/**/*.ts"]
}

isolatedModules: true and verbatimModuleSyntax: true force you to use import type / export type for type-only imports — which is exactly what a per-file transpiler requires.

Cache the transpiler in CI. Bun and Node both download quickly but not instantly. Cache ~/.bun/install/cache (or the Node toolchain) and pin the transpiler version in your CI image so the output is reproducible between local and CI.

Composer script convenience. Wire the build into Composer so nobody forgets it:

{
  "scripts": {
    "post-install-cmd": ["@typescript-build"],
    "typescript-build": "php bin/console typescript:build"
  }
}

Gotchas and Tips

  • No type checking from the bundle. Transpilers only erase types. A file that is 100% type-invalid still builds and ships. Enforce tsc --noEmit in CI; do not rely on the bundle for correctness.
  • isolatedModules semantics are enforced, not optional. File-by-file transpilation means import { Foo } from './types' where Foo is only a type will break at runtime unless you write import type { Foo }. Same for export type. Enable verbatimModuleSyntax to catch this at type-check time.
  • Duplicate .ts and .js assets. If the build emits .js next to the .ts sources under assets/, AssetMapper will expose both and can complain about conflicting logical paths. Either emit into a dedicated output directory (and point AssetMapper there via framework.asset_mapper.paths), or exclude .ts from the mapper's scan. Check with debug:asset-map — if you see two entries per entry point, this is why.
  • Production still needs the transpiler. The bundle shells out to Bun/Node at build time. If you compile on the deploy host, the binary must be installed there, and the bundle cannot live only in require-dev. If you compile in CI and ship artifacts, you can keep the toolchain off prod — but then your deploy must upload the emitted .js, not just the .ts sources.
  • Clear the AssetMapper cache when the mapping changes. Renaming a source file, adding an output directory, or switching transpiler versions can leave stale entries. php bin/console cache:clear (and re-running asset-map:compile) resolves most "it works in dev, 404 in prod" reports.
  • Source maps are not free. If you want usable stack traces in dev, source maps must be emitted by the transpiler and served by AssetMapper at a path the browser can resolve. Check whether your version of the bundle emits .js.map files and whether asset-map:compile copies them; adjust the output directory or disable source maps in prod to avoid shipping them.
  • Path and shell differences on Windows. The bundle invokes an external process. bun/node must be resolvable on the web/CLI user's PATH, and any config value that names the binary is a plain string — no shell expansion of ~ or environment variables. Test the build command as the same user that runs the web server or the deploy job.
  • Import map entries are not npm resolution. A successful tsc compile proves nothing about browser resolution. Whenever you add an npm import in TS, immediately run php bin/console importmap:require <pkg> and importmap:validate; otherwise your typecheck passes and the browser throws Failed to resolve module specifier.
  • Check config:dump-reference typescript after every upgrade. The package is at 39 stars with zero known dependents — the API surface is small but has moved between releases. Pinning a known-good version in composer.json and reading the changelog before upgrading is cheap insurance.
  • This bundle is not Encore. It only makes sense with AssetMapper's no-bundler model. Import maps, no CSS preprocessing bundled, no Webpack loaders. If you need those, stay on Webpack Encore or Vite and skip this package entirely.
  • Extension point if the transpiler is not enough. If you need a custom transform (e.g. a macro preprocessor), the compiler is a service tagged into asset_mapper.compiler; decorating or adding your own compiler next to the TypeScript one is the sanctioned way to extend the pipeline without forking the bundle.
  • Small ecosystem, small blast radius. With no dependents and a single maintainer, budget for reading source and opening issues. Keep the build reproducible (pinned transpiler, pinned bundle version) so an upstream change cannot silently alter emitted JavaScript.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core