or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

async-programming.mdcharacter-operations.mdcollections.mdconfiguration.mdcore-infrastructure.mddata-encoding.mddate-time.mdexternal-integration.mdindex.mdnumeric-types.mdreactive-programming.mdstring-operations.mdtype-system.md
tile.json

configuration.mddocs/

Configuration

Build configuration and module splitting setup for optimal JavaScript output, including TypeScript compilation settings, linting rules, and code splitting strategies.

TypeScript Configuration

The Fable library uses TypeScript for type safety and modern JavaScript features while targeting broad compatibility.

tsconfig.json - TypeScript Compiler Configuration

{
  "compilerOptions": {
    "target": "ES2015",
    "lib": ["ES2015", "DOM"],
    "module": "ES2015",
    "moduleResolution": "node",
    "declaration": false,
    "sourceMap": false,
    "outDir": "../../build/fable-library",
    "strict": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "allowJs": true
  },
  "include": [
    "**/*.ts",
    "**/*.js"
  ],
  "exclude": [
    "node_modules",
    "**/*.test.ts",
    "**/*.spec.ts"
  ]
}

Configuration Details

Target and Module Settings

// Target: ES2015 provides modern features while maintaining compatibility
// - Arrow functions, classes, const/let, template literals
// - Promises, Maps, Sets, Symbols
// - Destructuring, spread operator
// - Modules (import/export)

// Examples of ES2015 features used in Fable library:

// Arrow functions for callbacks
const mapper = (x: number) => x * 2;
const filtered = array.filter(x => x > 0);

// Classes for type definitions
class AsyncBuilder {
    Bind<T, U>(computation: IAsync<T>, binder: (x: T) => IAsync<U>): IAsync<U> {
        // Implementation using modern class syntax
    }
}

// Template literals for string formatting
function formatError(operation: string, error: Error): string {
    return `Operation '${operation}' failed: ${error.message}`;
}

// Destructuring and spread
function combineOptions(defaults: any, options: any): any {
    return { ...defaults, ...options };
}

// Maps and Sets for collections
const cache = new Map<string, any>();
const uniqueValues = new Set<number>();

// Promises for async operations (integrated with F# async)
function convertToPromise<T>(computation: IAsync<T>): Promise<T> {
    return new Promise((resolve, reject) => {
        // Implementation using native Promises
    });
}

Strict Type Checking

// strict: true enables all strict type checking options:

// 1. noImplicitAny - Variables must have explicit types
function processValue(value: any): string {  // Explicit 'any' required
    return value.toString();
}

// 2. strictNullChecks - null and undefined handling
function safeLookup<T>(map: Map<string, T>, key: string): T | undefined {
    return map.get(key); // Return type properly reflects possible undefined
}

// 3. strictFunctionTypes - Function parameter contravariance  
interface EventHandler<T> {
    (event: T): void;
}

// 4. strictBindCallApply - Strict bind/call/apply signatures
function bindMethod<T, Args extends any[], R>(
    obj: T, 
    method: (this: T, ...args: Args) => R
): (...args: Args) => R {
    return method.bind(obj);
}

// 5. strictPropertyInitialization - Class property initialization
class Timer {
    private intervalId!: number; // Definite assignment assertion when needed
    
    constructor(private interval: number) {
        this.start();
    }
    
    private start(): void {
        this.intervalId = setInterval(() => {}, this.interval);
    }
}

// 6. noImplicitReturns - All code paths must return
function classify(value: number): "positive" | "negative" | "zero" {
    if (value > 0) return "positive";
    if (value < 0) return "negative";
    return "zero"; // Required - no implicit return
}

Module Resolution

// moduleResolution: "node" enables Node.js-style module resolution
// This allows importing from npm packages and relative paths

// Import from npm packages
import * as bigJs from "big.js";

// Relative imports within the library
import { equals, compare } from "./Util.js";
import { SystemObject } from "./Types.js";

// Index file imports (resolves to index.ts/index.js)
import * as Collections from "./Collections/index.js";

// JSON imports (with resolveJsonModule: true)
import packageInfo from "./package.json";

// Type-only imports for better tree-shaking
import type { IComparer, IEquatable } from "./Util.js";

Development Configuration

Source Maps and Debugging

// sourceMap: false in production builds for smaller size
// Can be enabled for development:

{
  "compilerOptions": {
    "sourceMap": true,     // Enable for development
    "inlineSourceMap": false,
    "sourceRoot": "../src",
    "mapRoot": "./maps"
  }
}

// Usage in development tools:
// - Browser DevTools can show original TypeScript source
// - Stack traces reference original file locations
// - Breakpoints work in original source files

Declaration Files

// declaration: false to avoid generating .d.ts files
// F# generates its own type definitions through Fable compiler

// When enabled, would generate declarations like:
export declare class Long {
    constructor(low: number, high: number, unsigned?: boolean);
    low: number;
    high: number;
    unsigned: boolean;
    GetHashCode(): number;
    Equals(x: any): boolean;
    CompareTo(x: any): number;
}

export declare function add(left: Long, right: Long): Long;
export declare function subtract(left: Long, right: Long): Long;

Code Quality Configuration

tslint.json - Linting Rules

{
  "defaultSeverity": "error",
  "extends": [
    "tslint:recommended"
  ],
  "jsRules": {},
  "rules": {
    "semicolon": [true, "always"],
    "trailing-comma": [
      true,
      {
        "multiline": "always",
        "singleline": "never"
      }
    ],
    "quotemark": [true, "double"],
    "indent": [true, "spaces", 2],
    "max-line-length": [true, 120],
    "no-console": false,
    "no-var-requires": false,
    "object-literal-sort-keys": false,
    "ordered-imports": false,
    "interface-name": false,
    "no-namespace": false,
    "no-empty": false,
    "prefer-const": true,
    "curly": true,
    "eofline": true,
    "no-duplicate-imports": true,
    "no-unused-expression": true
  },
  "rulesDirectory": []
}

Key Linting Rules Explained

// 1. semicolon: [true, "always"] - Require semicolons
function example(): void {
    console.log("Statement must end with semicolon");  // ✓ Good
    // console.log("Missing semicolon")  // ✗ Error
}

// 2. quotemark: [true, "double"] - Use double quotes consistently
const message = "Use double quotes";  // ✓ Good
// const message = 'Single quotes not allowed';  // ✗ Error

// 3. indent: [true, "spaces", 2] - 2-space indentation
function nestedFunction(): void {
  if (true) {
    console.log("Properly indented");  // ✓ 2 spaces
  }
}

// 4. max-line-length: [true, 120] - Maximum line length
const longVariableName = "This line should not exceed 120 characters including all content and proper formatting";

// 5. trailing-comma: Consistent comma usage
const config = {
  option1: "value1",
  option2: "value2",  // ✓ Trailing comma in multiline
};

const singleLine = { a: 1, b: 2 };  // ✓ No trailing comma in single line

// 6. prefer-const: Use const for immutable bindings
const immutableValue = 42;  // ✓ Good
let mutableValue = 42;      // ✓ Good when modified later
// var oldStyle = 42;       // ✗ Prefer let/const over var

// 7. curly: Require braces for control structures
if (condition) {
  doSomething();  // ✓ Good - braces required
}

// if (condition) doSomething();  // ✗ Error - missing braces

// 8. no-duplicate-imports: Prevent duplicate import statements
import { ClassA, ClassB } from "./module.js";  // ✓ Good
// import { ClassA } from "./module.js";       // ✗ Error if ClassB imported elsewhere
// import { ClassB } from "./module.js";

Code Style Guidelines

Naming Conventions

// Class names: PascalCase
class AsyncBuilder { }
class MailboxProcessor<T> { }

// Interface names: PascalCase, may start with 'I'
interface IComparable<T> { }
interface Continuation<T> { }

// Function names: camelCase
function createCancellationToken(): CancellationToken { }
function startAsPromise<T>(computation: IAsync<T>): Promise<T> { }

// Variable names: camelCase
const defaultTimeout = 5000;
let currentState: string;

// Constants: SCREAMING_SNAKE_CASE or camelCase
const MAX_RETRY_ATTEMPTS = 3;
const defaultCancellationToken = new CancellationToken();

// Generic type parameters: Single uppercase letters
class Container<T, U, V> { }
function map<TInput, TOutput>(mapper: (x: TInput) => TOutput): TOutput { }

// File names: PascalCase for main modules, camelCase for utilities
// AsyncBuilder.ts, MailboxProcessor.ts
// util.ts, helpers.ts

Documentation and Comments

/**
 * Represents a 64-bit integer with arithmetic operations.
 * 
 * @example
 * ```typescript
 * const bigNumber = new Long(0x12345678, 0x9ABCDEF0);
 * const doubled = Long.multiply(bigNumber, Long.fromNumber(2));
 * ```
 */
class Long {
    /**
     * Creates a new Long instance.
     * 
     * @param low - The low 32 bits
     * @param high - The high 32 bits  
     * @param unsigned - Whether the number is unsigned
     */
    constructor(low: number, high: number, unsigned?: boolean) {
        // Implementation
    }

    /**
     * Compares this Long with another for equality.
     * 
     * @param other - The Long to compare with
     * @returns true if equal, false otherwise
     */
    Equals(other: any): boolean {
        // Implementation
    }
}

// Inline comments for complex logic
function complexCalculation(input: number[]): number {
    // Sort array for binary search optimization
    const sorted = input.sort((a, b) => a - b);
    
    // Binary search implementation
    let left = 0;
    let right = sorted.length - 1;
    
    while (left <= right) {
        const mid = Math.floor((left + right) / 2);
        // ... rest of implementation
    }
    
    return -1; // Not found
}

Build and Module Configuration

Module Splitting Configuration

// splitter.config.js - Code splitting configuration for optimal loading
module.exports = {
  // Entry points for different functional areas
  entries: {
    // Core functionality - loaded first
    core: [
      "./Util.ts",
      "./Types.js", 
      "./Option.js"
    ],
    
    // Numeric operations
    numeric: [
      "./Long.js",
      "./Int32.ts",
      "./Double.ts",
      "./Decimal.ts",
      "./BigInt.fs"
    ],
    
    // String and text processing
    text: [
      "./String.ts",
      "./RegExp.ts", 
      "./Char.ts",
      "./Unicode.9.0.0.ts"
    ],
    
    // Collections and sequences
    collections: [
      "./Seq.ts",
      "./Array.fs",
      "./List.fs",
      "./Set.fs",
      "./Map.fs",
      "./DictTypes.js"
    ],
    
    // Date and time
    datetime: [
      "./Date.ts",
      "./DateOffset.ts",
      "./TimeSpan.ts"
    ],
    
    // Async and reactive programming  
    async: [
      "./Async.ts",
      "./AsyncBuilder.ts",
      "./MailboxProcessor.ts",
      "./Observable.ts",
      "./Event.ts",
      "./Timer.ts"
    ],
    
    // Data encoding and I/O
    encoding: [
      "./Encoding.ts",
      "./BitConverter.ts",
      "./Uri.ts"
    ],
    
    // Type system and reflection
    reflection: [
      "./Reflection.ts"
    ]
  },
  
  // Output configuration
  output: {
    path: "./dist/chunks/",
    filename: "[name].bundle.js",
    chunkFilename: "[id].[chunkhash].chunk.js"
  },
  
  // Optimization settings
  optimization: {
    splitChunks: {
      chunks: "all",
      minSize: 20000,
      maxSize: 200000,
      cacheGroups: {
        // Vendor libraries
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: "vendors", 
          priority: 10,
          chunks: "all"
        },
        
        // Common utilities used across modules
        common: {
          name: "common",
          minChunks: 2,
          priority: 5,
          chunks: "all",
          enforce: true
        }
      }
    }
  }
};

Build Process Integration

Development Build Configuration

// webpack.config.dev.js - Development build settings
const path = require("path");
const splitterConfig = require("./splitter.config.js");

module.exports = {
  mode: "development",
  
  // Use splitter configuration for entries
  entry: splitterConfig.entries,
  
  output: {
    path: path.resolve(__dirname, "../../build/fable-library/dev"),
    filename: "[name].js",
    library: "FableLibrary",
    libraryTarget: "umd"
  },
  
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: "ts-loader",
        exclude: /node_modules/
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env"]
          }
        }
      }
    ]
  },
  
  resolve: {
    extensions: [".ts", ".js"],
    modules: [
      path.resolve(__dirname, "./"),
      "node_modules"
    ]
  },
  
  // Development optimizations
  devtool: "source-map",
  optimization: {
    minimize: false,
    ...splitterConfig.optimization
  },
  
  // Development server configuration
  devServer: {
    contentBase: path.join(__dirname, "../../build"),
    compress: true,
    port: 9000,
    hot: true
  }
};

Production Build Configuration

// webpack.config.prod.js - Production build settings
const path = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const splitterConfig = require("./splitter.config.js");

module.exports = {
  mode: "production",
  
  entry: splitterConfig.entries,
  
  output: {
    path: path.resolve(__dirname, "../../build/fable-library/prod"),
    filename: "[name].[contenthash].js",
    chunkFilename: "[id].[contenthash].chunk.js",
    library: "FableLibrary",
    libraryTarget: "umd",
    clean: true // Clean output directory before build
  },
  
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: {
          loader: "ts-loader",
          options: {
            configFile: "tsconfig.prod.json"
          }
        },
        exclude: /node_modules/
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: [
              ["@babel/preset-env", {
                targets: "> 0.25%, not dead",
                modules: false
              }]
            ],
            plugins: [
              "@babel/plugin-transform-runtime"
            ]
          }
        }
      }
    ]
  },
  
  resolve: {
    extensions: [".ts", ".js"],
    modules: [
      path.resolve(__dirname, "./"),
      "node_modules"
    ]
  },
  
  // Production optimizations
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: false, // Keep console for debugging
            drop_debugger: true,
            pure_funcs: ["console.debug"]
          },
          format: {
            comments: false
          }
        },
        extractComments: false
      })
    ],
    
    ...splitterConfig.optimization,
    
    // Additional production optimizations
    sideEffects: false,
    usedExports: true,
    
    // Runtime chunk for better caching
    runtimeChunk: "single"
  },
  
  // Performance hints
  performance: {
    hints: "warning",
    maxAssetSize: 200000,
    maxEntrypointSize: 200000
  }
};

Package Configuration

package.json Structure

{
  "name": "fable-library",
  "version": "1.0.0",
  "description": "F# runtime library for JavaScript/TypeScript",
  "main": "dist/core.js",
  "module": "dist/core.esm.js",
  "types": "dist/index.d.ts",
  
  "exports": {
    ".": {
      "import": "./dist/core.esm.js",
      "require": "./dist/core.js",
      "types": "./dist/index.d.ts"
    },
    "./Util": {
      "import": "./dist/Util.esm.js", 
      "require": "./dist/Util.js",
      "types": "./dist/Util.d.ts"
    },
    "./Long": {
      "import": "./dist/Long.esm.js",
      "require": "./dist/Long.js", 
      "types": "./dist/Long.d.ts"
    },
    "./String": {
      "import": "./dist/String.esm.js",
      "require": "./dist/String.js",
      "types": "./dist/String.d.ts" 
    }
  },
  
  "files": [
    "dist/",
    "lib/",
    "*.md"
  ],
  
  "scripts": {
    "build": "npm run build:dev && npm run build:prod",
    "build:dev": "webpack --config webpack.config.dev.js",
    "build:prod": "webpack --config webpack.config.prod.js",
    "typecheck": "tsc --noEmit",
    "lint": "tslint --project .",
    "lint:fix": "tslint --project . --fix",
    "test": "jest",
    "test:watch": "jest --watch",
    "clean": "rimraf dist build",
    "dev": "webpack-dev-server --config webpack.config.dev.js"
  },
  
  "dependencies": {
    "big.js": "^6.2.1"
  },
  
  "devDependencies": {
    "@babel/core": "^7.15.0",
    "@babel/preset-env": "^7.15.0", 
    "@babel/plugin-transform-runtime": "^7.15.0",
    "babel-loader": "^8.2.0",
    "typescript": "^4.4.0",
    "ts-loader": "^9.2.0", 
    "tslint": "^6.1.0",
    "webpack": "^5.50.0",
    "webpack-cli": "^4.8.0",
    "webpack-dev-server": "^4.0.0",
    "terser-webpack-plugin": "^5.2.0",
    "jest": "^27.0.0",
    "@types/jest": "^27.0.0",
    "rimraf": "^3.0.0"
  },
  
  "peerDependencies": {},
  
  "engines": {
    "node": ">=12.0.0"
  },
  
  "keywords": [
    "fsharp",
    "functional-programming", 
    "javascript",
    "typescript",
    "compiler",
    "runtime"
  ],
  
  "repository": {
    "type": "git",
    "url": "https://github.com/fable-compiler/fable-library.git"
  },
  
  "bugs": {
    "url": "https://github.com/fable-compiler/fable-library/issues"
  },
  
  "homepage": "https://fable.io",
  "license": "MIT"
}

Usage Examples

Import Strategies

// 1. Full library import (loads all modules)
import * as Fable from "fable-library";

// 2. Selective imports for better tree-shaking
import * as Util from "fable-library/Util";
import * as Long from "fable-library/Long";
import * as String from "fable-library/String";

// 3. Specific function imports
import { equals, compare } from "fable-library/Util";
import { fromNumber, add, multiply } from "fable-library/Long";

// 4. Dynamic imports for code splitting
async function loadNumericModule() {
    const { Long } = await import("fable-library/Long");
    return Long;
}

// 5. Node.js CommonJS imports
const Util = require("fable-library/Util");
const Long = require("fable-library/Long");

Build Size Optimization

// Bundle size optimization strategies:

// 1. Import only needed modules
import { equals } from "fable-library/Util";           // ~2KB
// import * as Util from "fable-library/Util";        // ~15KB 

// 2. Use specific entry points
import { fromNumber } from "fable-library/Long";       // ~8KB
// import * as FableLib from "fable-library";          // ~150KB+

// 3. Lazy loading for optional features
const loadBigIntModule = () => import("fable-library/BigInt");

async function performBigIntCalculation(value: string) {
    const { parse, multiply } = await loadBigIntModule();
    return multiply(parse(value), parse("2"));
}

// 4. Conditional imports based on feature flags
let DecimalModule: any;

if (FEATURE_FLAGS.highPrecisionMath) {
    DecimalModule = await import("fable-library/Decimal");
}