Skip to content

Instantly share code, notes, and snippets.

@drewwiens
drewwiens / rxjs-operators.ts
Last active July 29, 2022 16:56
Handy custom RxJS operators for Angular etc
import { AbstractControl, FormArray, FormGroup } from '@angular/forms';
import { map, toPairs, fromPairs, differenceWith, isEqual, isNull, isUndefined } from 'lodash';
import { Observable, OperatorFunction, defer, empty, of, merge, pipe } from 'rxjs';
import { distinctUntilChanged, filter, map, shareReplay, pairwise } from 'rxjs/operators';
/**
* Convenience RxJS operator that filters out undefined & null and modifies the downstream type
* appropriately.
*/
export function exists<T>(): OperatorFunction<T | undefined | null, T> {
@drewwiens
drewwiens / ng-dynamic-i18n.md
Last active April 14, 2022 20:14
(i18n) A simple way to dynamically load Angular translations

Problem

Calling loadTranslations() in either main.ts or in a resolved Promise does not work, at least in Angular 9 rc12. The function has to be called synchronously in polyfills.ts, so the translations also have to be available as a JS object, i.e. you can't fetch or XHR the JSON.

Solution

You could require() the JSON file in polyfills.ts, but then the JSON for all the languages will be in the bundle. (Try it and search for each JSON in polyfills.js.)

Alternatively, save each translation as a .js file and add a <script> tag to load only the desired translations.

@drewwiens
drewwiens / random-typescript.ts
Created November 16, 2020 16:34
Random useful TypeScript code snippets
/*
Ever need just the keys of an interface who's values match a certain type? e.g.
interface A {
id: string;
other: boolean:
}
we want a union of the keys of the properties with a value of type string (useful for strongly typing a mapping arrays of objects, to hashmap with a specific key value to be used as key in hashmap)
*/
type Unionize<T extends object> = {
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ACPI</key>
<dict>
<key>Add</key>
<array>
<dict>
<key>Comment</key>
@drewwiens
drewwiens / drag-drop.command.ts
Created March 1, 2021 20:45
Testing Angular Material drag and drop with Cypress
Cypress.Commands.add(
'dragDrop',
{ prevSubject: false },
(selector: string, originIdx: number, destIdx: number) => {
// Based on https://stackoverflow.com/a/55436989
const elements = Cypress.$(selector);
[originIdx, destIdx].forEach((idx) => {
if (originIdx < 0 || originIdx >= elements.length) {
throw Error(
`Cannot index element ${idx} of ${elements.length} draggable elements.`,
@drewwiens
drewwiens / compress-filename-in-url.js
Last active July 16, 2021 15:40
JavaScript String Compression of Filenames in a URL
// JavaScript String Compression of Filenames in a URL
//
// Problem: Reduce length of URL caused by storing a list of long filenames in URL query parameter.
//
// Solution: Switch between a 5-bit alphabet of letters and 4-bit alphabet of numbers. Use a special
// character to switch between lowercase and uppercase ("shift"). Represent the resulting bit
// string as a URL-safe base62 encoded string.
//
// Results: From my test set of ~100 strings from real filenames on a project:
// avg compression ratio: 0.87
@drewwiens
drewwiens / prune-node-modules.sh
Last active October 28, 2021 19:24
Simple shell script to prune unneeded packages from node_modules. For example, as a pre-processing step when building a Docker image of a Node app in an Nx monorepo that also contains frontend app(s). This reduced our Docker image by ~650 MB.
#!/bin/bash
# This script is somewhat unsafe, but may be useful. This script deletes dependencies
# in package.json by deleting any lines that match
# the regexp in the grep, runs yarn install --prod, and then reverts package.json and
# yarn.lock back to their original contents.
#
# This script assumes yarn 1.x, i.e. yarn classic. If using npm instead of yarn 1.x:
# 1. Replace "yarn.lock" with "package-lock.json"
# 2. Replace "yarn install --prod" with "npm install --only=prod"
@drewwiens
drewwiens / nestjs-expressjs-sub-app-sub-path.ts
Last active January 14, 2022 19:12
Serve a NestJS application from a sub-path using ExpressJS sub-app feature
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import { urlencoded, json } from 'express';
import { AppModule } from './app/app.module';
(async () => {
const nestExpressAppAdapter = new ExpressAdapter();
const app = await NestFactory.create(AppModule, nestExpressAppAdapter, {});
@drewwiens
drewwiens / serve-static-files.module.ts
Last active October 22, 2021 15:26
NestJS module to serve a single page app and set its base href from an environment variable on startup
import { join } from 'path';
import { Module } from '@nestjs/common';
import { ServeStaticModule } from '@nestjs/serve-static';
import { replaceInFile } from 'replace-in-file';
const STATIC_FILES_DIR = join(__dirname, 'client');
const YOUR_GLOBAL_PREFIX = 'api'; // Typically imported from some other file
@Module({
@drewwiens
drewwiens / app.module.ts
Last active April 1, 2022 16:27
Better NestJS pino-pretty logger output for local development
// Compact terminal output, colorized, with the following fields highlighted with
// brighter text for easier readability: request ID, request method, request URL,
// request Authorization header, response status code, and context which is usually
// a string passed when creating each logger object e.g. "SomeController".
import { Module } from '@nestjs/common';
import { blackBright, inverse } from 'cli-color';
import { pick } from 'lodash';
import { LoggerModule } from 'nestjs-pino';