Extract html-data; consider x86_64 when loading Java lib; use B2 action
This commit is contained in:
parent
8ff5bc3768
commit
2954f8f7b0
|
@ -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
|
||||
|
|
|
@ -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 }}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: |
|
||||
|
|
157
gen/attrs.ts
157
gen/attrs.ts
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in New Issue