Extract html-data; consider x86_64 when loading Java lib; use B2 action

This commit is contained in:
Wilson Lin 2020-11-13 22:15:51 +11:00
parent 8ff5bc3768
commit 2954f8f7b0
9 changed files with 37 additions and 3480 deletions

View File

@ -55,10 +55,8 @@ jobs:
npm install
./build.sh
- name: Install B2 CLI
run: |
sudo pip install setuptools
sudo pip install --upgrade b2
- name: Set up Backblaze B2 CLI
uses: wilsonzlin/setup-b2@v3
- name: Run bench and upload results
working-directory: ./bench

View File

@ -55,15 +55,8 @@ jobs:
- name: Build CLI
working-directory: ./cli
run: cargo build --release -vvv
- name: Install B2 CLI (macOS, Linux)
if: runner.os != 'Windows'
run: |
sudo pip install setuptools
sudo pip install --upgrade b2
- name: Install B2 CLI (Windows)
if: runner.os == 'Windows'
run: |
pip install --upgrade b2
- name: Set up Backblaze B2 CLI
uses: wilsonzlin/setup-b2@v3
- name: Upload to B2
run: |
b2 authorize-account ${{ secrets.CICD_CLI_B2_KEY_ID }} ${{ secrets.CICD_CLI_B2_APPLICATION_KEY }}

View File

@ -61,16 +61,8 @@ jobs:
npm run build
node compress.js
- name: Install B2 CLI (macOS, Linux)
if: runner.os != 'Windows'
run: |
sudo pip install setuptools
sudo pip install --upgrade b2
- name: Install B2 CLI (Windows)
if: runner.os == 'Windows'
run: |
pip install --upgrade b2
- name: Set up Backblaze B2 CLI
uses: wilsonzlin/setup-b2@v3
- name: Upload to B2
shell: bash

View File

@ -86,16 +86,8 @@ jobs:
cargo build -vvv --release
working-directory: ./ruby
- name: Install B2 CLI (macOS, Linux)
if: runner.os != 'Windows'
run: |
sudo pip install setuptools
sudo pip install --upgrade b2
- name: Install B2 CLI (Windows)
if: runner.os == 'Windows'
run: |
pip install --upgrade b2
- name: Set up Backblaze B2 CLI
uses: wilsonzlin/setup-b2@v3
- name: Upload to B2
run: |
@ -115,10 +107,8 @@ jobs:
uses: actions/setup-ruby@v1
with:
ruby-version: '2.5'
- name: Install B2 CLI
run: |
sudo pip install setuptools
sudo pip install --upgrade b2
- name: Set up Backblaze B2 CLI
uses: wilsonzlin/setup-b2@v3
- name: Pack and publish gem
working-directory: ./ruby
run: |

View File

@ -1,127 +1,23 @@
import {readFileSync, writeFileSync} from 'fs';
import ts, {Node, SourceFile, SyntaxKind, Type} from 'typescript';
import htmlData from '@wzlin/html-data';
import {writeFileSync} from 'fs';
import {join} from 'path';
import {DATA_DIR, prettyJson, RUST_OUT_DIR} from './_common';
const reactDeclarations = readFileSync(join(__dirname, 'data', 'react.d.ts'), 'utf8');
// TODO Consider and check behaviour when value matches case insensitively, after trimming whitespace, numerically (for number values), etc.
// TODO This file is currently manually sourced and written. Try to get machine-readable spec and automate.
const defaultAttributeValues: {
[attr: string]: {
tags: string[];
defaultValue: string;
isPositiveInteger?: boolean;
}[];
} = JSON.parse(readFileSync(join(DATA_DIR, 'attrs.json'), 'utf8'));
const tagNameNormalised = {
'anchor': 'a',
};
const attrNameNormalised = {
'classname': 'class',
};
const reactSpecificAttributes = [
'defaultChecked', 'defaultValue', 'suppressContentEditableWarning', 'suppressHydrationWarning',
];
const collapsibleAndTrimmable = {
'class': ['html:*'],
'd': ['svg:*'],
};
// TODO Is escapedText the API for getting name?
const getNameOfNode = (n: any) => n.name.escapedText;
const normaliseName = (name: string, norms: { [name: string]: string }) => [name.toLowerCase()].map(n => norms[n] || n)[0];
type AttrConfig = {
boolean: boolean;
redundantIfEmpty: boolean;
collapseAndTrim: boolean;
defaultValue?: string;
};
import {RUST_OUT_DIR} from './_common';
const rsTagAttr = ({
redundantIfEmpty,
defaultValue,
collapseAndTrim,
boolean,
}: AttrConfig) => `AttributeMinification { boolean: ${boolean}, redundant_if_empty: ${redundantIfEmpty}, collapse_and_trim: ${collapseAndTrim}, default_value: ${defaultValue == undefined ? 'None' : `Some(b"${defaultValue}")`} }`;
}: {
boolean: boolean;
redundantIfEmpty: boolean;
collapseAndTrim: boolean;
defaultValue?: string;
}) => `AttributeMinification { boolean: ${boolean}, redundant_if_empty: ${redundantIfEmpty}, collapse_and_trim: ${collapseAndTrim}, default_value: ${defaultValue
== undefined ? 'None' : `Some(b"${defaultValue}")`} }`;
const processReactTypeDeclarations = (source: SourceFile) => {
const nodes: Node[] = [source];
// Use index-based loop to keep iterating as nodes array grows.
for (let i = 0; i < nodes.length; i++) {
// forEachChild doesn't work if return value is number (e.g. return value of Array.prototype.push).
nodes[i].forEachChild(c => void nodes.push(c));
}
const attributeNodes = nodes
.filter(n => n.kind === ts.SyntaxKind.InterfaceDeclaration)
.map(n => [/^([A-Za-z]*)(HTML|SVG)Attributes/.exec(getNameOfNode(n)), n])
.filter(([matches]) => !!matches)
.map(([matches, node]) => [matches![2].toLowerCase(), normaliseName(matches![1], tagNameNormalised), node])
.filter(([namespace, tagName]) => namespace !== 'html' || !['all', 'webview'].includes(tagName))
.map(([namespace, tag, node]) => ({namespace, tag, node}))
.sort((a, b) => a.namespace.localeCompare(b.namespace) || a.tag.localeCompare(b.tag));
// Process global HTML attributes first as they also appear on some specific HTML tags but we don't want to keep the specific ones if they're global.
if (attributeNodes[0].namespace !== 'html' || attributeNodes[0].tag !== '') {
throw new Error(`Global HTML attributes is not first to be processed`);
}
// Map structure: attr => namespace => tag => config.
const attributes = new Map<string, Map<'html' | 'svg', Map<string, AttrConfig>>>();
for (const {namespace, tag, node} of attributeNodes) {
const fullyQualifiedTagName = [namespace, tag || '*'].join(':');
for (const n of node.members.filter((n: Node) => n.kind === ts.SyntaxKind.PropertySignature)) {
const attrName = normaliseName(getNameOfNode(n), attrNameNormalised);
if (reactSpecificAttributes.includes(attrName)) continue;
const types: SyntaxKind[] = n.type.kind === ts.SyntaxKind.UnionType
? n.type.types.map((t: Node) => t.kind)
: [n.type.kind];
const boolean = types.includes(ts.SyntaxKind.BooleanKeyword);
// If types includes boolean and string, make it a boolean attr to prevent it from being removed if empty value.
const redundantIfEmpty = !boolean && types.some(t => t === ts.SyntaxKind.StringKeyword || t === ts.SyntaxKind.NumberKeyword);
const defaultValues = (defaultAttributeValues[attrName] || [])
.filter(a => a.tags.includes(fullyQualifiedTagName))
.map(a => a.defaultValue);
const collapseAndTrim = (collapsibleAndTrimmable[attrName] || []).includes(fullyQualifiedTagName);
if (defaultValues.length > 1) {
throw new Error(`Tag-attribute combination <${fullyQualifiedTagName} ${attrName}> has multiple default values: ${defaultValues}`);
}
const attr: AttrConfig = {
boolean,
redundantIfEmpty,
collapseAndTrim,
defaultValue: defaultValues[0],
};
if (!attributes.has(attrName)) attributes.set(attrName, new Map());
const namespacesForAttribute = attributes.get(attrName)!;
if (!namespacesForAttribute.has(namespace)) namespacesForAttribute.set(namespace, new Map());
const tagsForNsAttribute = namespacesForAttribute.get(namespace)!;
if (tagsForNsAttribute.has(tag)) throw new Error(`Duplicate tag-attribute combination: <${fullyQualifiedTagName} ${attrName}>`);
const globalAttr = tagsForNsAttribute.get('*');
if (globalAttr) {
if (globalAttr.boolean !== attr.boolean
|| globalAttr.redundantIfEmpty !== attr.redundantIfEmpty
|| globalAttr.collapseAndTrim !== attr.collapseAndTrim
|| globalAttr.defaultValue !== attr.defaultValue) {
throw new Error(`Global and tag-specific attributes conflict: ${prettyJson(globalAttr)} ${prettyJson(attr)}`);
}
} else {
tagsForNsAttribute.set(tag || '*', attr);
}
}
}
let code = `
let code = `
use lazy_static::lazy_static;
use std::collections::HashMap;
use crate::spec::tag::ns::Namespace;
@ -173,34 +69,31 @@ impl AttrMap {
`;
code += `
code += `
lazy_static! {
pub static ref ATTRS: AttrMap = {
let mut m = HashMap::<&'static [u8], ByNamespace>::new();
${[...attributes].map(([attr_name, namespaces]) => ` m.insert(b\"${attr_name}\", ByNamespace {
${[...Object.entries(htmlData.attributes)].map(([attr_name, namespaces]) => ` m.insert(b\"${attr_name}\", ByNamespace {
${(['html', 'svg'] as const).map(ns => ` ${ns}: ` + (() => {
const tagsMap = namespaces.get(ns);
if (!tagsMap) {
return 'None';
}
const globalAttr = tagsMap.get('*');
if (globalAttr) {
return `Some(AttrMapEntry::AllNamespaceElements(${rsTagAttr(globalAttr)}))`;
}
return `Some({
const tagsMap = namespaces[ns];
if (!tagsMap) {
return 'None';
}
const globalAttr = tagsMap['*'];
if (globalAttr) {
return `Some(AttrMapEntry::AllNamespaceElements(${rsTagAttr(globalAttr)}))`;
}
return `Some({
let mut m = HashMap::<&'static [u8], AttributeMinification>::new();
${[...tagsMap].map(([tagName, tagAttr]) => ` m.insert(b\"${tagName}\", ${rsTagAttr(tagAttr)});`).join('\n')}
${Object.entries(tagsMap).map(([tagName, tagAttr]) => ` m.insert(b\"${tagName}\", ${rsTagAttr(tagAttr)});`).join('\n')}
AttrMapEntry::SpecificNamespaceElements(m)
})`;
})() + ',').join('\n')}
})() + ',').join('\n')}
});
`).join('')}
AttrMap::new(m)
};
}`;
return code;
};
const source = ts.createSourceFile(`react.d.ts`, reactDeclarations, ts.ScriptTarget.ES2020);
writeFileSync(join(RUST_OUT_DIR, 'attrs.rs'), processReactTypeDeclarations(source));
writeFileSync(join(RUST_OUT_DIR, 'attrs.rs'), code);

View File

@ -1,160 +0,0 @@
{
"align": [
{
"tags": [
"html:img"
],
"defaultValue": "bottom"
}
],
"decoding": [
{
"tags": [
"html:img"
],
"defaultValue": "auto"
}
],
"enctype": [
{
"tags": [
"html:form"
],
"defaultValue": "application/x-www-form-urlencoded"
}
],
"frameborder": [
{
"tags": [
"html:iframe"
],
"defaultValue": "1",
"isPositiveInteger": true
}
],
"formenctype": [
{
"tags": [
"html:button",
"html:input"
],
"defaultValue": "application/x-www-form-urlencoded"
}
],
"height": [
{
"tags": [
"html:iframe"
],
"defaultValue": "150",
"isPositiveInteger": true
}
],
"importance": [
{
"tags": [
"html:iframe"
],
"defaultValue": "auto"
}
],
"loading": [
{
"tags": [
"html:iframe",
"html:img"
],
"defaultValue": "eager"
}
],
"media": [
{
"tags": [
"html:style"
],
"defaultValue": "all"
}
],
"method": [
{
"tags": [
"html:form"
],
"defaultValue": "get"
}
],
"referrerpolicy": [
{
"tags": [
"html:iframe",
"html:img"
],
"defaultValue": "no-referrer-when-downgrade"
}
],
"rules": [
{
"tags": [
"html:table"
],
"defaultValue": "none"
}
],
"shape": [
{
"tags": [
"html:area"
],
"defaultValue": "rect"
}
],
"span": [
{
"tags": [
"html:col",
"html:colgroup"
],
"defaultValue": "1",
"isPositiveInteger": true
}
],
"target": [
{
"tags": [
"html:a",
"html:form"
],
"defaultValue": "_self"
}
],
"type": [
{
"tags": [
"html:button"
],
"defaultValue": "submit"
},
{
"tags": [
"html:input"
],
"defaultValue": "text"
},
{
"tags": [
"html:link",
"html:style"
],
"defaultValue": "text/css"
}
],
"width": [
{
"tags": [
"html:iframe"
],
"defaultValue": "300",
"isPositiveInteger": true
}
]
}

3150
gen/data/react.d.ts vendored

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
"private": true,
"dependencies": {
"@types/node": "^14.0.5",
"@wzlin/html-data": "^2020103004.0.1",
"ts-node": "^8.10.1",
"typescript": "^3.7.4",
"yaml": "^1.10.0"

View File

@ -25,7 +25,7 @@ public class MinifyHtml {
: osName.startsWith("mac")
? "macos"
: null;
String nativeLibNameArch = osArch.equals("amd64") ? "x86_64" : null;
String nativeLibNameArch = osArch.equals("amd64") || osArch.equals("x86_64") ? "x86_64" : null;
if (nativeLibNameOs == null || nativeLibNameArch == null) {
throw new RuntimeException(format("Platform not supported (os.name=%s, os.arch=%s)", osName, osArch));