npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@cis-badvilbel/twig-monaco-editor

v1.0.3

Published

IntelliSense for Twig within Micros0ofts Monaco-Editor

Readme

Installation

npm require @cis-badvilbel/twig-monaco-editor

Features

  • Auto-completion of Twig 3.x control structures, filters and functions
  • Extension of autocomplete with your own definitions (filters, functions, variables)
  • When using nested variables/objects, filters (such as last, first or filter) are "ignored".

Usage

Using just the Twig-Completion-Editor-Class

The following minimal example enables the use of the Monaco editor including auto-completion for the Twig control structures, filters and functions.

<div id="editor"></div>
import {TwigMonacoEditor} from 'twig-monaco-editor'

document.addEventListener('DOMContentLoaded', () => {
    const editor = new TwigMonacoEditor('editor', {})
})

The first option is the ID of the HTML DOM element that is to be replaced by the editor.

The second option are further options that are used to instantiate the editor. See Monaco Editor documentation for more details. Only the language is set to twig and does not need to be stored or is automatically overwritten.

Using only the Completion-Provider

It is also possible to use only the completion provider, which also enables use in a language other than Twig:

import {twigCompletionProvider} from 'twig-monaco-editor'
import * as monaco from 'monaco-editor'

monaco.languages.registerCompletionItemProvider('twig', new twigCompletionProvider(
    customCompletionOptions,
    customFilterOptions,
    customFunctionOptions,
    defaultCompletionKind
))

All options are optional.

The following descriptions of the functions also apply when using the provider directly.

Adding Custom Completion Options

Twig offers the option of defining your own filters or functions. These can be integrated into the Twig Monaco editor alongside your own variables.

The following object structure is used internally for this purpose:

type CompletionOption = {
    label?: string,
    insertText?: string|insertTextSnippet,
    insertTextRules?: CompletionItemInsertTextRule
    kind?: CompletionItemKind,
    detail?: string,
    hint?: string,
    documentation?: string,
    subOptions?: CompletionOptions|CompletionItem[],
    is_list?: boolean,
    range?: Range
}

It follows the structure of the CompletionItem of the Monaco editor.

The transfer to the Twig Monaco editor takes place as a CompletionOptions object, which represents a simple assignment of the label to the CompletionOption:

{
    label: { ... },
    variable: { ... }
}

Internally, the properties are adopted or "calculated" via default values, if not defined.

  • Label and insertText are taken from the key if not specified.
  • The kind can be specified or the default value is used. The default value can be passed to the constructor as the 6th option, otherwise it is languages.CompletionItemKind.Property.
  • Range is filled in automatically when the suggestion list is created and does not need to be entered.
  • Detail is string in case it is not defined
  • SubOptions is again an object with the pairing of Key and CompletionOption.

Example

The following example is a minimal example of an auto-completion of individual variables or variables/objects with corresponding properties:

const completion = {
    device: {
        subOptions: {
            id: {
                detail: 'integer'
            },
            hostname: {},
            ip_address: {},
            interfaces: {
                subOptions: {
                    name: {},
                    access_vlan: {}
                }
            }
        }
    },
    locations: {
        subOptions: {
            name: {},
            address: {},
            contact: {}
        }
    }
}

Initialization with own completion options

const editor = new TwigMonacoEditor(
    'editor',
    {},
    <customCompletion/VariableOptions>,
    <customFilterOptions>,
    <customFunctionOptions>,
    <defaultCompletionKind>
)

Custom functions/filters with snippet/tab support

The definition of your own functions or filters with parameters can also be stored as a code snippet so that tab stops can be set up and used.

All you need to do is define insertText as follows:

{
    functionName: {
        insertText: { value: "functionName(${1}, ${2})${0}"}
    }
}

All tab stops are defined consecutively with ${i}. ${0} is then the final position after you have finished.

The required InsertTextRule is automatically set to the correct value monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet.

Variables in FOR loops and in SET statements

If variables are defined in set statements or for loops, these variables are also added to the list of suggestions. Assignments to nested variables are adopted.

When reassigning variables to nested objects, all filters that do not contain a space are automatically filtered out. For the filter filter, the complete statement is filtered out and a sub-assignment is possible, e.g:

{% for deviceInterfaces in devices|filter((d) => d.name == 'router1').interfaces %}

This example only shows the basic mode of operation and deviceInterfaces refers to the definition in devices.subOptions.interfaces in its own customCompletionOptions object.

With For loops, the scope (you are inside the loop) is taken into account.

For set statements, the scope (within a {% with %}) is currently not observed.

Access to editor instance

Access to the editor instance is still possible via the editor property:

const twigEditor = new TwigMonacoEditor('editor', {})

const value = twigEditor.editor.getValue()
twigEditor.editor.setValue('Replacing the content')

Using with Vite: Splitting the JS files

It is recommended not to use the standard build settings for Vite, as this leads to duplications and unnecessarily large index files, which usually slow down the initial loading of the page.

With the following settings, which are only an example and must be adapted to your own project, the index files are kept as small as possible and at the same time duplicated code for the Monaco editor (which is around 3-6MB in size when unpacked) is kept to a minimum.

export default defineConfig({
    build: {
        rollupOptions: {
            output: {
                manualChunks(id, meta) {
                    if (id.includes('twig-monaco-editor')) {
                        return 'twig-monaco-editor'
                    } else if (id.includes('monaco-editor')) {
                        return 'monaco-editor'
                    } else {
                        return 'vendor' // Or do what you think fits your code best
                    }
                }
            }
        }
    },
    resolve: {
        dedupe: ['monaco-editor'] // This ensures, that twig-monaco-editor and monaco-editor don't include the same source
    }
})

The above example provides the following output variables in a small example:

dist/index.html                                 1.49 kB │ gzip:   0.56 kB
dist/assets/codicon-DCmgc-ay.ttf               80.34 kB
dist/assets/index-CRb4mluJ.css                131.64 kB │ gzip:  20.66 kB
dist/assets/monaco-editor-BMrrEDdW.css        133.02 kB │ gzip:  21.10 kB
dist/assets/index-DQXZqKF-.js                   1.99 kB │ gzip:   1.04 kB
dist/assets/twig-monaco-editor-Df71ywh5.js      9.67 kB │ gzip:   2.88 kB
dist/assets/monaco-editor-BITGtLfy.js       3,912.93 kB │ gzip: 993.61 kB

With the automatic packaging of Vite, some parts of the Monaco editor are outsourced, but the Monaco editor, together with the Twig Monaco editor, is also packed as a duplicate in index.js:

dist/index.html                               1.10 kB │ gzip:     0.50 kB
dist/assets/codicon-DCmgc-ay.ttf             80.34 kB
dist/assets/index-seylN-Z1.css              213.40 kB │ gzip:    34.46 kB
dist/assets/azcli-BaLxmfj-.js                 1.09 kB │ gzip:     0.46 kB
dist/assets/azcli-D1avlpzM.js                 1.09 kB │ gzip:     0.46 kB
...
~ 165 files with about 0.5-8kB
...
dist/assets/jsonMode-DQXoFORo.js             41.78 kB │ gzip:    12.13 kB
dist/assets/jsonMode-DY4fHppG.js             41.78 kB │ gzip:    12.13 kB
dist/assets/index-qhdgBbuR.js             6,686.61 kB │ gzip: 1,719.00 kB

This reduces the loading speed, as the browser has to load the main components completely before the page is displayed. The browser can only load the other data, including the Monaco editor, asynchronously by splitting it up.