Skip to content

Commit

Permalink
Enhance build (compact CSS), add 'AttachmentModel' interface
Browse files Browse the repository at this point in the history
  • Loading branch information
adanski committed Jan 22, 2023
1 parent 095f7cb commit eb05801
Show file tree
Hide file tree
Showing 14 changed files with 117 additions and 53 deletions.
1 change: 0 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ jobs:
node-version: ${{matrix.node-version}}
- run: npm install
- run: npm run build
- run: npm run bundle
test:
needs: build
uses: ./.github/workflows/test.yml
1 change: 0 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ jobs:
registry-url: https://registry.npmjs.org/
- run: npm install
- run: npm run build
- run: npm run bundle
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
41 changes: 41 additions & 0 deletions compact-css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import MagicString, {SourceMap} from 'magic-string';
import {readFileSync, writeFileSync} from 'node:fs';

let stylesheet: string = '';
let bundle: string = '';

try {
stylesheet = readFileSync('./dist/css/stylesheet.js', 'utf8');
bundle = readFileSync('./dist/bundle/comments-element-esm.js', 'utf8');
} catch (e) {
console.error(e);
process.exit(1);
}

const stylesheetMagicString: MagicString = compactCss(stylesheet);
const stylesheetCode: string = stylesheetMagicString.toString();
const stylesheetMap: SourceMap = stylesheetMagicString.generateMap({file: 'stylesheet.js', source: '../../src/css/stylesheet.ts'});
const bundleMagicString: MagicString = compactCss(bundle);
const bundleCode: string = bundleMagicString.toString();
const bundleMap: SourceMap = bundleMagicString.generateMap({file: 'comments-element-esm.js'});

try {
writeFileSync('./dist/css/stylesheet.js', stylesheetCode, 'utf8');
writeFileSync('./dist/css/stylesheet.js.map', stylesheetMap.toString(), 'utf8');
writeFileSync('./dist/bundle/comments-element-esm.js', bundleCode, 'utf8');
writeFileSync('./dist/bundle/comments-element-esm.js.map', bundleMap.toString(), 'utf8');
} catch (e) {
console.error(e);
process.exit(1);
}

function compactCss(source: string): MagicString {
const magicString: MagicString = new MagicString(source);

// Primitive regexp to select CSS strings to minify
const multiLineCssString: RegExp = /tagNoop\s*`\s*[a-z*.#/][^`]+\{[^`]+\}\s*`/gm;
return magicString.replaceAll(multiLineCssString, css => css
.replace(/\s+/gm, ' ')
.replace(/tagNoop\s*`/g, '`')
.trim());
}
18 changes: 10 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,20 @@
}
},
"scripts": {
"build": "tsc --project src/tsconfig.json",
"build:watch": "tsc --watch --project src/tsconfig.json",
"bundle": "esbuild src/index.ts --bundle --format=esm --platform=browser --target=esnext --outfile=dist/bundle/comments-element-esm.js --legal-comments=none",
"build": "npm run build:tsc && npm run build:bundle && npm run build:css",
"build:tsc": "tsc --project src/tsconfig.json",
"build:bundle": "esbuild src/index.ts --bundle --format=esm --platform=browser --target=esnext --outfile=dist/bundle/comments-element-esm.js --legal-comments=none",
"build:css": "ts-node --esm -P tsconfig-template.json -O {\\\"moduleResolution\\\":\\\"node\\\"} compact-css.ts",
"test": "node --experimental-vm-modules node_modules/jest/bin/jest"
},
"files": [
"dist/*.js",
"dist/*.ts",
"dist/*.map",
"dist/css",
"dist/options",
"dist/subcomponent",
"dist/bundle",
"dist/css/",
"dist/options/",
"dist/subcomponent/",
"dist/bundle/",
"src/",
"tsconfig-template.json"
],
Expand Down Expand Up @@ -69,6 +70,7 @@
"jest-environment-jsdom": "~29.3.1",
"ts-jest": "~29.0.3",
"ts-node": "~10.9.1",
"esbuild": "~0.16.14"
"esbuild": "~0.16.14",
"magic-string": "~0.27.0"
}
}
29 changes: 15 additions & 14 deletions src/css/stylesheet.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const mainStyle: string = `
import {tagNoop} from './tag-noop.js';

const mainStyle: string = tagNoop`
#comments-container * {
box-sizing: border-box;
text-shadow: none;
Expand Down Expand Up @@ -568,7 +570,7 @@ const mainStyle: string = `
`;

/* Content */
const contentStyle: string = `
const contentStyle: string = tagNoop`
#comments-container ul.main li.comment .wrapper .content {
white-space: pre-line;
word-break: break-word;
Expand All @@ -588,7 +590,7 @@ const contentStyle: string = `
`;

/* Attachments */
const attachmentsStyle: string = `
const attachmentsStyle: string = tagNoop`
#comments-container ul.main li.comment .wrapper .attachments .tags:not(:empty) {
margin-bottom: 0.5em;
}
Expand All @@ -612,7 +614,7 @@ const attachmentsStyle: string = `
`;

/* Actions */
const actionsStyle: string = `
const actionsStyle: string = tagNoop`
#comments-container.mobile ul.main li.comment .actions {
font-size: 1em;
}
Expand Down Expand Up @@ -663,7 +665,7 @@ const actionsStyle: string = `
`;

/* Child comments */
const childCommentsStyle: string = `
const childCommentsStyle: string = tagNoop`
/* Margin for second level content */
#comments-container ul.main li.comment .child-comments > *::before,
#comments-container ul.main li.comment ax-commenting-field > *::before {
Expand Down Expand Up @@ -736,7 +738,7 @@ const childCommentsStyle: string = `
`;

/* Editing comment */
const editingCommentStyle: string = `
const editingCommentStyle: string = tagNoop`
#comments-container ul.main li.comment.edit > .comment-wrapper > *:not(.commenting-field) {
display: none;
}
Expand All @@ -748,33 +750,32 @@ const editingCommentStyle: string = `
`;

/* Drag & drop attachments */
const dragAndDropAttachmentsStyle: string = `
const dragAndDropAttachmentsStyle: string = tagNoop`
#comments-container.drag-ongoing {
overflow-y: hidden !important;
}
#comments-container .droppable-overlay {
display: table;
display: flex;
align-items: center;
justify-content: center;
position: fixed;
z-index: 99;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.3)
background: rgba(0,0,0,0.65)
}
#comments-container .droppable-overlay .droppable-container {
display: table-cell;
vertical-align: middle;
text-align: center;
}
#comments-container .droppable-overlay .droppable-container .droppable {
background: #FFF;
color: #CCC;
padding: 6em;
padding: 10em;
}
#comments-container .droppable-overlay .droppable-container .droppable.drag-over {
Expand All @@ -787,7 +788,7 @@ const dragAndDropAttachmentsStyle: string = `
`;

/* Read-only mode */
const readOnlyStyle: string = `
const readOnlyStyle: string = tagNoop`
#comments-container.read-only .commenting-field {
display: none;
}
Expand Down
9 changes: 9 additions & 0 deletions src/css/tag-noop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Noop tagger used just to tag own CSS literals.
*/
export function tagNoop(...args: [TemplateStringsArray, ...(string | number)[]]): string {
let s: string = args[0][0];
for (let i: number = 1, l = args.length; i < l; i++)
s += arguments[i] + args[0][i];
return s;
}
4 changes: 2 additions & 2 deletions src/options/callbacks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {CommentModel, PingableUser, ReferenceableHashtag} from './models.js';
import {AttachmentModel, CommentModel, PingableUser, ReferenceableHashtag} from './models.js';
import {Functionalities} from './functionalities.js';

export interface Callbacks {
Expand Down Expand Up @@ -216,7 +216,7 @@ export interface Callbacks {
* };
* ```
*/
validateAttachments?(attachments: any[], accept: AcceptFct<any[]>): void;
validateAttachments?(attachments: AttachmentModel<File>[], accept: AcceptFct<AttachmentModel<File>[]>): void;
/**
* A callback `function` that is called after user has clicked a `#` hashtag
*
Expand Down
8 changes: 7 additions & 1 deletion src/options/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export interface CommentModel {
modifiedAt?: Date;
content: string;
// Required if attachments are enabled
attachments?: object[];
attachments?: AttachmentModel[];
// Required if pinging is enabled
pings?: UserDisplayNamesById;
creatorUserId: string;
Expand Down Expand Up @@ -55,3 +55,9 @@ export interface ReferenceableHashtag {
*/
description?: string;
}

export interface AttachmentModel<F extends File | string = File | string> {
id: string;
file: F;
mimeType: string;
}
9 changes: 5 additions & 4 deletions src/subcomponent/comment-container-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {TextareaElement} from './textarea-element.js';
import {CommentElement} from './comment-element.js';
import {SuccessFct} from '../options/callbacks.js';
import {CommentTransformer} from '../comment-transformer.js';
import {AttachmentModel} from '../options/models.js';

@RegisterCustomElement('ax-comment-container')
export class CommentContainerElement extends HTMLElement implements WebComponent {
Expand Down Expand Up @@ -169,13 +170,13 @@ export class CommentContainerElement extends HTMLElement implements WebComponent
attachments.append(attachmentPreviews, attachmentTags);

if (this.#options.enableAttachments && this.commentModel.hasAttachments()) {
this.commentModel.attachments?.forEach((attachment: any) => {
(this.commentModel.attachments as AttachmentModel<string>[])?.forEach(attachment => {
let format = undefined;
let type = undefined;

// Type and format
if (attachment.mime_type) {
const mimeTypeParts = attachment.mime_type.split('/');
if (attachment.mimeType) {
const mimeTypeParts = attachment.mimeType.split('/');
if (mimeTypeParts.length === 2) {
format = mimeTypeParts[1];
type = mimeTypeParts[0];
Expand All @@ -202,7 +203,7 @@ export class CommentContainerElement extends HTMLElement implements WebComponent
video.controls = true;
const videoSource: HTMLSourceElement = document.createElement('source');
videoSource.src = attachment.file;
videoSource.type = attachment.mime_type;
videoSource.type = attachment.mimeType;
video.append(videoSource);
preview.append(video);
}
Expand Down
18 changes: 10 additions & 8 deletions src/subcomponent/commenting-field-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import {TextcompleteFactory} from './textcomplete-factory.js';
import {ErrorFct, SuccessFct} from '../options/callbacks.js';
import {CommentTransformer} from '../comment-transformer.js';
import {AttachmentModel} from '../options/models.js';

@RegisterCustomElement('ax-commenting-field')
export class CommentingFieldElement extends HTMLElement implements WebComponent {
Expand Down Expand Up @@ -69,7 +70,7 @@ export class CommentingFieldElement extends HTMLElement implements WebComponent
#initElement(): void {
let profilePictureURL: string | undefined;
let userId: string;
let attachments: Record<string, any>[];
let attachments: AttachmentModel[];

this.classList.add('commenting-field');
if (this.isMain) {
Expand Down Expand Up @@ -315,7 +316,7 @@ export class CommentingFieldElement extends HTMLElement implements WebComponent
const time: Date = new Date();

const commentModel: CommentModel = {
id: 'c' + (this.#commentViewModel.getComments().length + 1), // Temporary id
id: 'tempId_' + (this.#commentViewModel.getComments().length + 1), // Temporary id
parentId: textarea.parentId || undefined,
createdAt: time,
modifiedAt: time,
Expand All @@ -333,9 +334,9 @@ export class CommentingFieldElement extends HTMLElement implements WebComponent
return commentModel;
}

getAttachments(): any[] {
getAttachments<F extends File | string>(): AttachmentModel<F>[] {
const attachmentElements: NodeListOf<HTMLAnchorElement> = this.querySelectorAll('.attachments .attachment');
const attachments: any[] = [];
const attachments: AttachmentModel<F>[] = [];
for (let i = 0; i < attachmentElements.length; i++) {
attachments[i] = (attachmentElements[i] as any).attachmentTagData;
}
Expand Down Expand Up @@ -391,13 +392,14 @@ export class CommentingFieldElement extends HTMLElement implements WebComponent
return;
}
// Create attachment models
let attachments: any[] = [...files].map(file => ({
mime_type: file.type,
let attachments: AttachmentModel<File>[] = [...files].map(file => ({
id: 'tempId_' + file.name,
mimeType: file.type,
file: file
}));

// Filter out already added attachments
const existingAttachments: any[] = this.getAttachments();
const existingAttachments: AttachmentModel<File>[] = this.getAttachments();
attachments = attachments.filter(attachment => {
let duplicate = false;

Expand All @@ -422,7 +424,7 @@ export class CommentingFieldElement extends HTMLElement implements WebComponent
uploadButton.setButtonState(false, true);

// Validate attachments
this.#options.validateAttachments(attachments, (validatedAttachments: any[]) => {
this.#options.validateAttachments(attachments, validatedAttachments => {

if (validatedAttachments.length) {
// Create attachment tags
Expand Down
13 changes: 4 additions & 9 deletions src/subcomponent/tag-factory.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import {ButtonElement} from './button-element.js';
import {CommentsOptions} from '../api.js';
import {OptionsProvider} from '../provider.js';
import {CommentingFieldElement} from './commenting-field-element.js';
import {findParentsBySelector} from '../html-util.js';
import {AttachmentModel} from '../options/models.js';

export class TagFactory {

Expand Down Expand Up @@ -30,19 +29,15 @@ export class TagFactory {
return tagEl;
}

createAttachmentTagElement(attachment: Record<string, any>, onDeleted?: () => void): HTMLAnchorElement {
createAttachmentTagElement(attachment: AttachmentModel, onDeleted?: () => void): HTMLAnchorElement {
// Tag element
const attachmentTag: HTMLAnchorElement = document.createElement('a');
attachmentTag.classList.add('tag', 'attachment');
attachmentTag.target = '_blank';

// Bind data
attachmentTag.setAttribute('id', attachment.id);
(attachmentTag as any).attachmentTagData = {
id: attachment.id,
mime_type: attachment.mime_type,
file: attachment.file,
};
(attachmentTag as any).attachmentTagData = attachment;

// File name
let fileName: string = '';
Expand Down Expand Up @@ -83,7 +78,7 @@ export class TagFactory {
}, 'delete');
attachmentTag.append(closeButton);
} else { // Set href attribute if not deletable
attachmentTag.setAttribute('href', attachment.file);
attachmentTag.setAttribute('href', attachment.file as string);
}

return attachmentTag;
Expand Down
2 changes: 1 addition & 1 deletion src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function isMobileBrowser(): boolean {
* Converts non-standard spaces to regular spaces and trims every space/newline/return at both ends
*/
export function normalizeSpaces(inputText: string): string {
return inputText.trim().replace(/[^\S\n]+/g, ' ');
return inputText.trim().replace(/([^\S\n ]|[^\P{C}\n ]|[^\P{Z}\n ])/gmu, ' ');
}

export function debounce<T extends (...args: any[]) => void>(callback: T, wait: number, options: DebounceOptions = DebounceOptions.BOTH_EDGES): T {
Expand Down
9 changes: 9 additions & 0 deletions test/css/tag-noop.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {describe, expect, it} from '@jest/globals';
import {tagNoop} from '../../src/css/tag-noop.js';

describe('tagNoop', () => {
it('Should return unchanged string', () => {
const four: string = '4';
expect(tagNoop`${1}23${four}`).toEqual('1234');
});
});
Loading

0 comments on commit eb05801

Please sign in to comment.