Skip to content

Commit

Permalink
feat(prefabs): google webfonts support (#127)
Browse files Browse the repository at this point in the history
  • Loading branch information
roperzh committed May 19, 2020
1 parent e4b134d commit bb3bafa
Show file tree
Hide file tree
Showing 44 changed files with 4,505 additions and 433 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@
"test": "lerna run test",
"watch": "yarn diez watch",
"prepublishOnly": "yarn clean",
"prepack": "yarn diez minify-sources"
"prepack": "yarn diez minify-sources",
"regenerate-goldens": "./scripts/regenerate-goldens.sh"
},
"resolutions": {
"@types/webpack": "4.4.27",
Expand Down
10 changes: 10 additions & 0 deletions scripts/regenerate-goldens.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash -f

# Clean generated files from packages.
for package in compiler/targets framework/stdlib; do
TARGET=src/$package
pushd $TARGET
yarn regenerate-goldens
pushd
echo "regenerated goldens for $TARGET"
done
10 changes: 10 additions & 0 deletions src/compiler/compiler-core/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,16 @@ export abstract class Compiler<
return;
}

if (
instance &&
instance.targets &&
Array.isArray(instance.targets) &&
!instance.targets.includes(this.parser.options.target)
) {
// We are looking at an instance that is explicitly excluded by the host.
return;
}

const targetComponent = this.createTargetComponent(name);
const serializedData = serialize(instance);

Expand Down
4 changes: 4 additions & 0 deletions src/compiler/targets/sources/web/styles.css.handlebars
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
{{#each resources as |resource|}}
@import url("{{{resource}}}");
{{/each}}

{{#each styleFonts as |font|}}
@font-face {
{{#each font.declaration}}
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/targets/sources/web/styles.scss.handlebars
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
{{#each resources as |resource|}}
@import url("{{{resource}}}");
{{/each}}

{{#each styleFonts as |font|}}
@font-face {
{{#each font.declaration}}
Expand Down
9 changes: 9 additions & 0 deletions src/compiler/targets/src/targets/web.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ export interface StyleTokens {
styleVariables: StyleVariableToken[];
styleFonts: Rule[];
styleSheets: Rule[];
resources: string[];
}

/**
* Represents an external web resource.
*/
interface WebExternalResource {
url: string;
}

/**
Expand All @@ -123,6 +131,7 @@ export interface WebOutput extends TargetOutput<WebDependency, WebBinding> {
declarations: Set<string>;
declarationImports: Set<string>;
styleSheet: StyleSheet;
resources: Map<string, WebExternalResource>;
serializedTree: Record<string, any>;
}

Expand Down
2 changes: 2 additions & 0 deletions src/compiler/targets/src/targets/web.compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ export class WebCompiler extends Compiler<WebOutput, WebBinding> {
declarationImports: new Set<string>(),
dependencies: new Set<WebDependency>(),
assetBindings: new Map(),
resources: new Map(),
serializedTree: {},
styleSheet: {
variables: new Map(),
Expand Down Expand Up @@ -305,6 +306,7 @@ export class WebCompiler extends Compiler<WebOutput, WebBinding> {
styleVariables,
styleFonts: this.output.styleSheet.font.serialize(),
styleSheets: this.output.styleSheet.styles.serialize(),
resources: [...this.output.resources.values()].map((resource) => resource.url),
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@


:root {
--primitives-number: 10;
--primitives-number-px: 10px;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@


$primitives-number: 10;
$primitives-number-px: 10px;
$primitives-number-pt: 10pt;
Expand Down
7 changes: 6 additions & 1 deletion src/framework/engine/src/prefab.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {PropertyOptions, Serializable} from './api';
import {PropertyOptions, Serializable, Target} from './api';
import {serialize} from './serialization';

/**
Expand All @@ -16,6 +16,11 @@ export abstract class Prefab<T extends object> implements Serializable<T> {
*/
host?: Prefab<any>;

/**
* The list of targets this prefab should target.
*/
protected targets?: Target[];

/**
* Every concrete extension must implement exhaustive defaults conforming to the data interface.
*/
Expand Down
7 changes: 7 additions & 0 deletions src/framework/prefabs/.diezrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"providers": {
"commands": [
"./scripts/lib/commands/generate-google-fonts"
]
}
}
4 changes: 3 additions & 1 deletion src/framework/prefabs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"test": "jest",
"health": "jest --ci -c jest.ci.js && tslint -p . -t checkstyle -o checkstyle-result.xml",
"lint": "tslint -p . -t stylish",
"fix": "tslint -p . -t stylish --fix"
"fix": "tslint -p . -t stylish --fix",
"generate-google-fonts": "tsc -p scripts && yarn diez generate-google-fonts"
},
"dependencies": {
"@diez/engine": "^10.5.2",
Expand All @@ -30,6 +31,7 @@
"istanbul-reporter-cobertura-haiku": "^1.0.4",
"jest": "^24.7.1",
"jest-tap-reporter": "^1.9.0",
"phin": "^3.4.1",
"ts-jest": "^24.1.0",
"tslint": "^5.15.0",
"tslint-config-haiku": "^1.0.21",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {CliAction} from '@diez/cli-core';
import {writeFile} from 'fs-extra';
import {join} from 'path';
import phin from 'phin';
import {FontCollectionGenerator, GoogleFontParser} from '../utils';

interface GoogleFontsFamily {
family: string;
variants: string[];
}

interface GoogleFontsCollection {
items: GoogleFontsFamily[];
}

interface GenerateGoogleFontsParams {
apiKey: string;
}

type RequestLib = (options: {url: string, parse?: 'json'}) => Promise<{body: GoogleFontsCollection}>;

/**
* Fetch all fonts available in Google Fonts via their Developer API.
*/
export const fetchGoogleFontsFromApi = async (apiKey: string, requestLib = phin as RequestLib) => {
const response = await requestLib({
url: `https://www.googleapis.com/webfonts/v1/webfonts?key=${apiKey}&sort=popularity`,
parse: 'json',
});

return response.body.items;
};

const generateGoogleFontsAction: CliAction = async ({apiKey}: GenerateGoogleFontsParams) => {
const availableFonts = await fetchGoogleFontsFromApi(apiKey);
const parser = new GoogleFontParser();

for (const {family, variants} of availableFonts) {
for (const variant of variants) {
parser.parse(family, variant);
}
}

await writeFile(
join('src', 'resources', 'web-google-fonts.ts'),
FontCollectionGenerator.generateTypeScriptEnum(parser),
);
};

export default generateGoogleFontsAction;
21 changes: 21 additions & 0 deletions src/framework/prefabs/scripts/commands/generate-google-fonts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {CliCommandProvider} from '@diez/cli-core';

const provider: CliCommandProvider = {
name: 'generate-google-fonts',
description: 'Generates a collection of Font prefab instances with all available Google Fonts.',
loadAction: () => import('./generate-google-fonts.action'),
options: [
{
description: 'Your Google Developer API key (required)',
longName: 'apiKey',
valueName: 'apiKey',
validator: async (options) => {
if (!options.apiKey) {
throw new Error('apiKey is required.');
}
},
},
],
};

export = provider;
10 changes: 10 additions & 0 deletions src/framework/prefabs/scripts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../../tsconfig.shared.json",
"compilerOptions": {
"rootDir": ".",
"outDir": "lib"
},
"include": [
"**/**/*.ts"
]
}
86 changes: 86 additions & 0 deletions src/framework/prefabs/scripts/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {pascalCase} from 'change-case';

const FontWeightName: Record<number, string> = {
100: 'Thin',
200: 'ExtraLight',
300: 'Light',
400: 'Regular',
500: 'Medium',
600: 'SemiBold',
700: 'Bold',
800: 'ExtraBold',
900: 'Black',
};

const DiezFontStyle: Record<string, string> = {
regular: 'FontStyle.Normal',
italic: 'FontStyle.Italic',
bold: 'FontStyle.Bold',
};

/**
* Abstraction to parse font data from different sources into an internal
* standard format.
*
* @ignore
*/
abstract class FontCollectionParser {
readonly abstract name: string;
readonly abstract instanceConstructor: string;
collection = new Map<string, string>();

protected abstract parseVariation (variant: string): {style: string, weight: number};

protected addToCollection (family: string, style: string, weight: number) {
this.collection.set(
pascalCase(`${family}-${FontWeightName[weight]}${weight}-${style === 'regular' ? '' : style}`),
`${this.instanceConstructor}('${family}', {weight: ${weight}, style: ${DiezFontStyle[style]}})`,
);
}

parse (family: string, variant: string) {
const {style, weight} = this.parseVariation(variant);
this.addToCollection(family, style, weight);
}
}

/**
* Parses responses from the Google Fonts Developer API into a format that can
* be easily consumed.
*
* @ignore
*/
export class GoogleFontParser extends FontCollectionParser {
readonly name = 'GoogleWebFonts';
readonly instanceConstructor = 'Font.googleWebFont';

protected parseVariation (variation: string) {
const weight = variation.match(/^([0-9]+)/);
const style = variation.match(/([A-Za-z]+)$/);
return {weight: weight ? Number(weight[0]) : 400, style: style ? style[0] : 'regular'};
}
}

/**
* Generates output based on data from a [[FontCollectionParser]]
*
* @ignore
*/
export class FontCollectionGenerator {
static generateTypeScriptEnum (parser: FontCollectionParser) {
const entries = [];
for (const [font, initializer] of parser.collection.entries()) {
entries.push(`${font}: ${initializer}`);
}

return `import {Font, FontStyle} from '../font';
/**
* As a convenience, this enumeration provides the names of all the core fonts supported on ${this.name}.
*/
export const ${parser.name} = {
${entries.join(',\n ')},
};
`;
}
}
1 change: 1 addition & 0 deletions src/framework/prefabs/src/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const enum FileType {
Raw = 'raw',
Image = 'image',
Font = 'font',
Remote = 'remote',
}

/**
Expand Down
Loading

0 comments on commit bb3bafa

Please sign in to comment.