Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ importmap.yaml
*.bak
*.env
!*.map
qr.js
89 changes: 1 addition & 88 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,93 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Add `@aegisjsproject/dev-server`

### Changed
- Update CSP, template, etc

## [v1.1.3] - 2025-11-21

### Added
- Add `npm start` script

### Changed
- Update npm publishing

## [v1.1.2] - 2025-05-01

### Changed
- Use `eslint` & `rollup` directly instead of by other packages
- Update node version via `.npmrc`
- Update Node CI workflow
- Install & use `@shgysk8zer0/eslint-config`
- Add support for `node --test`, including ignoring tests for publishing
- Update ESLint & super-linter
- Switch to more basic Rollup config
- Update `exports` and `main` accordingly

### Fixed
- Fix missed renaming in README

## Removed
- Remove old ESLint config files

## [v1.1.1] - 2023-09-24

### Added
- Add `unpkg` to `package.json`
- Add badges in README

### Changed
- Update `exports` to `package.json` to handle wider variety

### Fixed
- Fix typo in `fix:js` script

### [v1.1.0] - 2023-07-03

### Changed
- Update to node 20
- Update npm publishing GH Action

## [v1.0.5] - 2023-07-02

### Added
- Add `funding`

### Changed
- Updated GitHub Actions workflows
- Update versioning & lock-file scripts
- Update `.npmignore` & `.gitignore`

## [v1.0.4] - 2023-06-08

### Added
- Install `@shgysk8zer0/npm-utils`
- Add `exports` to package config

### Removed
- Uninstall `rollup`, `eslint`

### Changed
- Use `getConfig()` from `@shgysk8zer0/js-utils/rollup` for rollup config

## [v1.0.3] - 2023-06-01

### Fixed
- Revert to old Release Action, now with permissions & link to changelog

## [v1.0.2] - 2023-06-01

### Fixed
- Fix `changelog-entry` to match `[$version]` instead of `$version`

## [v1.0.1] - 2023-05-31

### Fixed
- Update GitHub Release workflow to use [Auto Release](https://github.com/marketplace/actions/auto-release)

## [v1.0.0] - 2023-05-31
## [v1.0.0] - 2025-12-23

Initial Release
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
# npm-template
# `@aegisjsproject/qr-encoder`

A template repo for npm packages
A QR encoder for node and browsers

[![CodeQL](https://github.com/shgysk8zer0/npm-template/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/shgysk8zer0/npm-template/actions/workflows/codeql-analysis.yml)
![Node CI](https://github.com/shgysk8zer0/npm-template/workflows/Node%20CI/badge.svg)
![Lint Code Base](https://github.com/shgysk8zer0/npm-template/workflows/Lint%20Code%20Base/badge.svg)
[![CodeQL](https://github.com/AegisJSProject/qr-encoder/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/AegisJSProject/qr-encoder/actions/workflows/codeql-analysis.yml)
![Node CI](https://github.com/AegisJSProject/qr-encoder/workflows/Node%20CI/badge.svg)
![Lint Code Base](https://github.com/AegisJSProject/qr-encoder/workflows/Lint%20Code%20Base/badge.svg)

[![GitHub license](https://img.shields.io/github/license/shgysk8zer0/npm-template.svg)](https://github.com/shgysk8zer0/npm-template/blob/master/LICENSE)
[![GitHub last commit](https://img.shields.io/github/last-commit/shgysk8zer0/npm-template.svg)](https://github.com/shgysk8zer0/npm-template/commits/master)
[![GitHub release](https://img.shields.io/github/release/shgysk8zer0/npm-template?logo=github)](https://github.com/shgysk8zer0/npm-template/releases)
[![GitHub license](https://img.shields.io/github/license/AegisJSProject/qr-encoder.svg)](https://github.com/AegisJSProject/qr-encoder/blob/master/LICENSE)
[![GitHub last commit](https://img.shields.io/github/last-commit/AegisJSProject/qr-encoder.svg)](https://github.com/AegisJSProject/qr-encoder/commits/master)
[![GitHub release](https://img.shields.io/github/release/AegisJSProject/qr-encoder?logo=github)](https://github.com/AegisJSProject/qr-encoder/releases)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/shgysk8zer0?logo=github)](https://github.com/sponsors/shgysk8zer0)

[![npm](https://img.shields.io/npm/v/@shgysk8zer0/npm-template)](https://www.npmjs.com/package/@shgysk8zer0/npm-template)
![node-current](https://img.shields.io/node/v/@shgysk8zer0/npm-template)
![npm bundle size gzipped](https://img.shields.io/bundlephobia/minzip/@shgysk8zer0/npm-template)
[![npm](https://img.shields.io/npm/dw/@shgysk8zer0/npm-template?logo=npm)](https://www.npmjs.com/package/@shgysk8zer0/npm-template)
[![npm](https://img.shields.io/npm/v/@aegisjsproject/qr-encoder)](https://www.npmjs.com/package/@aegisjsproject/qr-encoder)
![node-current](https://img.shields.io/node/v/@aegisjsproject/qr-encoder)
![npm bundle size gzipped](https://img.shields.io/bundlephobia/minzip/@aegisjsproject/qr-encoder)
[![npm](https://img.shields.io/npm/dw/@aegisjsproject/qr-encoder?logo=npm)](https://www.npmjs.com/package/@aegisjsproject/qr-encoder)

[![GitHub followers](https://img.shields.io/github/followers/shgysk8zer0.svg?style=social)](https://github.com/shgysk8zer0)
![GitHub forks](https://img.shields.io/github/forks/shgysk8zer0/npm-template.svg?style=social)
![GitHub stars](https://img.shields.io/github/stars/shgysk8zer0/npm-template.svg?style=social)
![GitHub forks](https://img.shields.io/github/forks/AegisJSProject/qr-encoder.svg?style=social)
![GitHub stars](https://img.shields.io/github/stars/AegisJSProject/qr-encoder.svg?style=social)
[![Twitter Follow](https://img.shields.io/twitter/follow/shgysk8zer0.svg?style=social)](https://twitter.com/shgysk8zer0)

[![Donate using Liberapay](https://img.shields.io/liberapay/receives/shgysk8zer0.svg?logo=liberapay)](https://liberapay.com/shgysk8zer0/donate "Donate using Liberapay")
Expand Down
1 change: 0 additions & 1 deletion consts.js

This file was deleted.

24 changes: 4 additions & 20 deletions http.config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { imports } from '@shgysk8zer0/importmap';
import { checkCacheItem, setCacheItem } from '@aegisjsproject/http-utils/cache.js';
import { addTrustedTypePolicy, addScriptSrc, useDefaultCSP } from '@aegisjsproject/http-utils/csp.js';
import { addScriptSrc, addTrustedTypePolicy, useDefaultCSP } from '@aegisjsproject/http-utils/csp.js';

addScriptSrc(
'https://unpkg.com/@aegisjsproject/',
'https://unpkg.com/@shgysk8zer0/',
imports['@shgysk8zer0/polyfills'],
imports.qr,
);

addTrustedTypePolicy('aegis-sanitizer#html');

export default {
Expand All @@ -15,19 +13,5 @@ export default {
'/favicon.svg': '@aegisjsproject/dev-server/favicon',
},
open: true,
requestPreprocessors: [
'@aegisjsproject/http-utils/request-id.js',
checkCacheItem,
],
responsePostprocessors: [
'@aegisjsproject/http-utils/compression.js',
'@aegisjsproject/http-utils/cors.js',
useDefaultCSP(),
(response, { request }) => {
if (request.destination === 'document') {
response.headers.append('Link', `<${imports['@shgysk8zer0/polyfills']}>; rel="preload"; as="script"; fetchpriority="high"; crossorigin="anonymous"; referrerpolicy="no-referrer"`);
}
},
setCacheItem,
],
responsePostprocessors: [useDefaultCSP()],
};
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<link rel="icon" href="/favicon.svg" type="image/svg+xml" sizes="any" />
<script type="importmap" integrity="{{ INTEGRITY }}">{{ IMPORTMAP }}</script>
<script src="{{ POLYFILLS }}" referrerpolicy="no-referrer" crossorigin="anonymous" defer=""></script>
<script src="/qr.js" type="module"></script>
</head>
<body>
<header id="header">
Expand Down
110 changes: 107 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,107 @@
import { MESSAGE } from 'npm-template/consts.js';
console.log(import.meta);
console.info(MESSAGE);
import encodeQR from 'qr';
const namespace = 'http://www.w3.org/2000/svg';

const SANITIZER = {
allowElements: [
{ name: 'svg', namespace },
{ name: 'path', namespace },
{ name: 'rect', namespace }],
allowAttributes: {
'viewBox': ['svg'],
'xmlns': ['svg'],
'd': ['path'],
'fill': ['svg', 'rect'],
'x': ['rect'],
'y': ['rect'],
'width': ['*'],
'height': ['*'],
}
};
const ECC = 'medium';
const BORDER = 4;
const SCALE = 4;
const GIF = 'gif';
const SVG = 'svg';
const FILL = '#000';
const BACKGROUND = '#fff';
const SIZE = 480;

const attr = str => {
return String(str ?? '').replace(/[&<>"']/g, (char) => ({
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
'\'': '&#39;'
}[char]));
};

export function createGIF(input, {
ecc = ECC,
border = BORDER,
scale = SCALE,
} = {}) {
return encodeQR(input, GIF, { ecc, border, scale });
}

export const createGIFBlob = (input, {
ecc = ECC,
border = BORDER,
scale = SCALE,
} = {}) => new Blob([createGIF(input, { ecc, border, scale })], { type: 'image/gif' });

export const createGIFFile = (input, name = 'qr.gif', {
ecc = ECC,
border = BORDER,
scale = SCALE,
} = {}) => new File([createGIF(input, { ecc, border, scale })], name, { type: 'image/gif' });

export function createSVGString(input, {
ecc = ECC,
border = BORDER,
scale = SCALE,
size = SIZE,
fill = FILL,
background = BACKGROUND
} = {}) {
const s = attr(size);

return encodeQR(input, SVG, { ecc, border, scale })
.replace('<svg ', `<svg fill="${attr(fill)}" height="${s}" width="${s}" `)
.replace(/(<svg[^>]*>)/i, `$1\n\t<rect x="0" y="0" fill="${attr(background)}" height="100%" width="100%" />\n`);
}

export const createSVGBlob = (input, {
ecc = ECC,
border = BORDER,
scale = SCALE,
fill = FILL,
background = BACKGROUND,
size = SIZE,
} = {}) => new Blob([createSVGString(input, { ecc, border, scale, fill, background, size })], { type: 'image/svg+xml' });

export const createSVGFile = (input, name = 'qr.svg', {
ecc = ECC,
border = BORDER,
scale = SCALE,
fill = FILL,
background = BACKGROUND,
size = SIZE,
} = {}) => new File([createSVGString(input, { ecc, border, scale, fill, background, size })], name, { type: 'image/svg+xml' });

export function createSVGElement(input, {
ecc = ECC,
border = BORDER,
scale = SCALE,
fill = FILL,
background = BACKGROUND,
size = SIZE,
} = {}) {
// eslint-disable-next-line no-undef
const el = document.createElement('div');
el.setHTML(
createSVGString(input, { ecc, border, scale, fill, background, size }),
{ sanitizer: SANITIZER }
);
return el.firstElementChild;
}
32 changes: 29 additions & 3 deletions index.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
import { test, describe } from 'node:test';
import assert from 'node:assert';
import { MESSAGE } from './consts.js';
import { createSVGBlob, createSVGFile, createGIFFile, createGIFBlob } from './index.js';
const signal = AbortSignal.timeout(300);
const input = 'https://github.com/AegisJSProject/qr-encoder';

describe('An example test', () => {
test('Message is a string', { signal }, () => assert.equal(typeof MESSAGE, 'string', 'Message should be a string.'));
describe('Test generating QR codes', () => {
test('Test generating SVG Blobs', { signal }, () => {
const result = createSVGBlob(input);
assert.ok(result instanceof Blob, 'Should output a `Blob`.');
assert.equal(result?.type, 'image/svg+xml', 'Should have an SVG type.');
assert.notEqual(result?.size, 0, 'Should not be empty.');
});
test('Test generating SVG Files', { signal }, () => {
const result = createSVGFile(input);
assert.ok(result instanceof File, 'Should output a `Blob`.');
assert.equal(result?.type, 'image/svg+xml', 'Should have an SVG type.');
assert.notEqual(result?.size, 0, 'Should not be empty.');
assert.ok(result?.name?.endsWith('.svg'), 'SVG files should have the `.svg` extension.');
});
test('Test generating GIF Blobs', { signal }, () => {
const result = createGIFBlob(input);
assert.ok(result instanceof Blob, 'Should output a `Blob`.');
assert.equal(result?.type, 'image/gif', 'Should have an SVG type.');
assert.notEqual(result?.size, 0, 'Should not be empty.');
});
test('Test generating GIF Files', { signal }, () => {
const result = createGIFFile(input);
assert.ok(result instanceof File, 'Should output a `Blob`.');
assert.equal(result?.type, 'image/gif', 'Should have an GIF type.');
assert.notEqual(result?.size, 0, 'Should not be empty.');
assert.ok(result?.name?.endsWith('.gif'), 'SVG files should have the `.gif` extension.');
});
});
Loading
Loading