A browser based code editor that powers Visual Studio Code
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
JSON schema validation, CSS/SCSS/Less support, and HTML/Handlebars language features in Monaco Editor.
Monaco provides comprehensive JSON support with schema validation and IntelliSense.
import * as monaco from 'monaco-editor';
// Access JSON defaults
const jsonDefaults = monaco.languages.json.jsonDefaults;monaco.languages.json.jsonDefaults: LanguageServiceDefaultsConfiguration object for JSON language service.
jsonDefaults.setDiagnosticsOptions(options: DiagnosticsOptions): voidConfigures JSON validation and diagnostics.
interface DiagnosticsOptions {
validate?: boolean;
allowComments?: boolean;
schemas?: JSONSchema[];
enableSchemaRequest?: boolean;
hoverSettings?: HoverSettings;
completionSettings?: CompletionSettings;
}// Configure JSON validation
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
allowComments: false,
schemas: [
{
uri: 'http://json-schema.org/draft-07/schema#',
fileMatch: ['*'],
schema: {
type: 'object',
properties: {
name: { type: 'string' },
version: { type: 'string' },
dependencies: {
type: 'object',
additionalProperties: { type: 'string' }
}
}
}
}
],
enableSchemaRequest: true
});interface JSONSchema {
uri: string;
fileMatch?: string[];
schema?: any;
}// Package.json schema example
const packageJsonSchema = {
uri: 'http://json.schemastore.org/package',
fileMatch: ['package.json'],
schema: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'The name of the package'
},
version: {
type: 'string',
pattern: '^\\d+\\.\\d+\\.\\d+',
description: 'Version must be a valid semver string'
},
main: {
type: 'string',
description: 'The main entry point'
},
scripts: {
type: 'object',
additionalProperties: {
type: 'string'
},
description: 'Script commands'
},
dependencies: {
type: 'object',
additionalProperties: {
type: 'string'
},
description: 'Package dependencies'
},
devDependencies: {
type: 'object',
additionalProperties: {
type: 'string'
},
description: 'Development dependencies'
}
},
required: ['name', 'version']
}
};
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
schemas: [packageJsonSchema]
});// Create a JSON model with schema validation
const packageJsonModel = monaco.editor.createModel(`{
"name": "my-project",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "jest"
},
"dependencies": {
"express": "^4.18.0"
},
"devDependencies": {
"jest": "^28.0.0"
}
}`, 'json', monaco.Uri.parse('file:///package.json'));Monaco supports CSS, SCSS, and Less with IntelliSense and validation.
// Access CSS defaults
const cssDefaults = monaco.languages.css.cssDefaults;
const scssDefaults = monaco.languages.css.scssDefaults;
const lessDefaults = monaco.languages.css.lessDefaults;cssDefaults.setOptions(options: LanguageServiceOptions): voidConfigures CSS language service options.
interface LanguageServiceOptions {
validate?: boolean;
lint?: LintSettings;
data?: CSSDataConfiguration;
hoverSettings?: HoverSettings;
completionSettings?: CompletionSettings;
}// Configure CSS options
monaco.languages.css.cssDefaults.setOptions({
validate: true,
lint: {
compatibleVendorPrefixes: 'ignore',
vendorPrefix: 'warning',
duplicateProperties: 'warning',
emptyRules: 'warning',
importStatemements: 'ignore',
boxModel: 'ignore',
universalSelector: 'ignore',
zeroUnits: 'ignore',
fontFaceProperties: 'warning',
hexColorLength: 'error',
argumentsInColorFunction: 'error',
unknownProperties: 'warning',
ieHack: 'ignore',
unknownVendorSpecificProperties: 'ignore',
propertyIgnoredDueToDisplay: 'warning',
important: 'ignore',
float: 'ignore',
idSelector: 'ignore'
}
});
// Configure SCSS options
monaco.languages.css.scssDefaults.setOptions({
validate: true,
lint: {
// SCSS-specific lint rules
unknownAtRules: 'ignore' // Allow SCSS @rules
}
});
// Configure Less options
monaco.languages.css.lessDefaults.setOptions({
validate: true,
lint: {
// Less-specific lint rules
unknownAtRules: 'ignore' // Allow Less @rules
}
});interface CSSDataConfiguration {
version: 1;
properties?: IPropertyData[];
atDirectives?: IAtDirectiveData[];
pseudoClasses?: IPseudoClassData[];
pseudoElements?: IPseudoElementData[];
}// Add custom CSS properties
const customCSSData = {
version: 1,
properties: [
{
name: '--primary-color',
description: 'Primary theme color',
browsers: ['FF', 'S', 'C', 'IE', 'E', 'O']
},
{
name: '--secondary-color',
description: 'Secondary theme color',
browsers: ['FF', 'S', 'C', 'IE', 'E', 'O']
}
],
atDirectives: [
{
name: '@custom-media',
description: 'Define custom media queries',
browsers: ['FF', 'S', 'C']
}
]
};
monaco.languages.css.cssDefaults.setOptions({
data: customCSSData
});// CSS model
const cssModel = monaco.editor.createModel(`
:root {
--primary-color: #3498db;
--secondary-color: #2ecc71;
--font-size: 16px;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
font-size: var(--font-size);
}
.button {
background-color: var(--primary-color);
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.button:hover {
background-color: var(--secondary-color);
}
@media (max-width: 768px) {
.container {
padding: 10px;
}
}
`, 'css', monaco.Uri.parse('file:///styles.css'));
// SCSS model
const scssModel = monaco.editor.createModel(`
$primary-color: #3498db;
$secondary-color: #2ecc71;
$border-radius: 4px;
%button-base {
border: none;
padding: 10px 20px;
border-radius: $border-radius;
cursor: pointer;
transition: background-color 0.3s ease;
}
.button {
@extend %button-base;
background-color: $primary-color;
color: white;
&:hover {
background-color: darken($primary-color, 10%);
}
&.secondary {
background-color: $secondary-color;
&:hover {
background-color: darken($secondary-color, 10%);
}
}
}
@mixin respond-to($breakpoint) {
@if $breakpoint == mobile {
@media (max-width: 768px) { @content; }
}
@if $breakpoint == tablet {
@media (min-width: 769px) and (max-width: 1024px) { @content; }
}
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
@include respond-to(mobile) {
padding: 10px;
}
}
`, 'scss', monaco.Uri.parse('file:///styles.scss'));
// Less model
const lessModel = monaco.editor.createModel(`
@primary-color: #3498db;
@secondary-color: #2ecc71;
@border-radius: 4px;
.button-mixin(@bg-color) {
background-color: @bg-color;
border: none;
padding: 10px 20px;
border-radius: @border-radius;
cursor: pointer;
transition: background-color 0.3s ease;
&:hover {
background-color: darken(@bg-color, 10%);
}
}
.button {
.button-mixin(@primary-color);
color: white;
&.secondary {
.button-mixin(@secondary-color);
}
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
@media (max-width: 768px) {
padding: 10px;
}
}
`, 'less', monaco.Uri.parse('file:///styles.less'));Monaco provides HTML support with tag completion, attribute validation, and embedded CSS/JavaScript.
// Access HTML defaults
const htmlDefaults = monaco.languages.html.htmlDefaults;
const handlebarsDefaults = monaco.languages.html.handlebarDefaults;
const razorDefaults = monaco.languages.html.razorDefaults;htmlDefaults.setOptions(options: HTMLLanguageServiceOptions): voidConfigures HTML language service options.
interface HTMLLanguageServiceOptions {
format?: HTMLFormatConfiguration;
suggest?: HTMLSuggestOptions;
data?: HTMLDataConfiguration;
}// Configure HTML options
monaco.languages.html.htmlDefaults.setOptions({
format: {
tabSize: 2,
insertSpaces: true,
wrapLineLength: 120,
unformatted: 'default',
contentUnformatted: 'pre,code,textarea',
indentInnerHtml: false,
preserveNewLines: true,
maxPreserveNewLines: 2,
indentHandlebars: false,
endWithNewline: false,
extraLiners: 'head, body, /html'
},
suggest: {
html5: true,
angular1: false,
ionic: false
}
});interface HTMLDataConfiguration {
version: 1;
tags?: ITagData[];
attributes?: IAttributeData[];
valueSets?: IValueSet[];
}// Add custom HTML elements and attributes
const customHTMLData = {
version: 1,
tags: [
{
name: 'my-component',
description: 'Custom component',
attributes: [
{
name: 'prop1',
description: 'First property',
valueSet: 'string'
},
{
name: 'prop2',
description: 'Second property',
valueSet: 'boolean'
}
]
}
],
attributes: [
{
name: 'data-testid',
description: 'Test identifier for automated testing',
valueSet: 'string'
}
]
};
monaco.languages.html.htmlDefaults.setOptions({
data: customHTMLData
});// HTML model
const htmlModel = monaco.editor.createModel(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Monaco Editor Example</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
}
.container {
max-width: 800px;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="container">
<h1>Welcome to Monaco Editor</h1>
<p>This is a demonstration of HTML editing capabilities.</p>
<my-component prop1="value1" prop2="true" data-testid="demo-component">
<span>Custom component content</span>
</my-component>
<button onclick="handleClick()">Click me</button>
</div>
<script>
function handleClick() {
alert('Button clicked!');
}
</script>
</body>
</html>
`, 'html', monaco.Uri.parse('file:///index.html'));
// Handlebars model
const handlebarsModel = monaco.editor.createModel(`
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
</head>
<body>
<h1>{{heading}}</h1>
{{#if user}}
<p>Welcome, {{user.name}}!</p>
<p>Email: {{user.email}}</p>
{{else}}
<p>Please log in.</p>
{{/if}}
<ul>
{{#each items}}
<li>
<strong>{{this.name}}</strong>
{{#if this.description}}
- {{this.description}}
{{/if}}
</li>
{{/each}}
</ul>
{{> footer}}
</body>
</html>
`, 'handlebars', monaco.Uri.parse('file:///template.hbs'));All language services provide common IntelliSense features:
Language services run in web workers for performance:
self.MonacoEnvironment = {
getWorkerUrl: function (moduleId, label) {
if (label === 'json') {
return './json.worker.bundle.js';
}
if (label === 'css' || label === 'scss' || label === 'less') {
return './css.worker.bundle.js';
}
if (label === 'html' || label === 'handlebars' || label === 'razor') {
return './html.worker.bundle.js';
}
return './editor.worker.bundle.js';
}
};// Configure all language services
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
allowComments: true,
schemas: [
{
uri: 'http://json-schema.org/package',
fileMatch: ['package.json'],
schema: packageJsonSchema
}
]
});
monaco.languages.css.cssDefaults.setOptions({
validate: true,
lint: {
duplicateProperties: 'warning',
unknownProperties: 'warning'
}
});
monaco.languages.html.htmlDefaults.setOptions({
format: {
tabSize: 2,
insertSpaces: true
},
suggest: {
html5: true
}
});
// Create models for different languages
const models = [
monaco.editor.createModel(jsonContent, 'json', monaco.Uri.parse('file:///config.json')),
monaco.editor.createModel(cssContent, 'css', monaco.Uri.parse('file:///styles.css')),
monaco.editor.createModel(htmlContent, 'html', monaco.Uri.parse('file:///index.html'))
];
// Create editor and switch between models
const editor = monaco.editor.create(document.getElementById('container'));
editor.setModel(models[0]); // Start with JSON