Compare commits

..

No commits in common. "main" and "tableclass" have entirely different histories.

21 changed files with 103 additions and 2164 deletions

2
.eslintignore Normal file
View File

@ -0,0 +1,2 @@
node_modules/
dist/

18
.eslintrc.json Normal file
View File

@ -0,0 +1,18 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"parserOptions": {
"project": "./tsconfig.json"
},
"rules": {
"no-console": "off"
}
}

View File

@ -1,13 +1,7 @@
# bom2table
Generate a nicely formatted table from an EAGLE BOM
## Usage
Run `npm install` and `npm build`
Upload a BOM exported in csv format from EAGLE (untick 'List attributes') to get a table and the corresponding HTML.
## Credits
Favicon by [Round Icons](https://unsplash.com/illustrations/a-computer-chip-with-a-red-yellow-and-green-color-scheme-otzESK2sBG4?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash) via Unsplash

View File

@ -1,17 +0,0 @@
// @ts-check
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
export default tseslint.config(
{
ignores: [
'dist/**',
'node_modules/**',
'webpack.dev.js',
'webpack.prod.js',
],
},
eslint.configs.recommended,
tseslint.configs.recommended,
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -4,10 +4,6 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
<title>Read CSV File</title>
</head>
<body>

View File

@ -1 +0,0 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}

2070
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +1,24 @@
{
"name": "bom2table",
"version": "2.1.0",
"version": "1.3.0",
"description": "",
"main": "bom2table.js",
"scripts": {
"serve": "webpack serve --config webpack.dev.js",
"build": "webpack --config webpack.prod.js",
"lint": "eslint ."
"build": "webpack --config webpack.prod.js"
},
"author": "nplayfair",
"license": "MIT",
"dependencies": {
"csvtojson": "^2.0.10",
"htmlfy": "^0.7.5"
"csvtojson": "^2.0.10"
},
"devDependencies": {
"@eslint/js": "^9.28.0",
"@types/csvtojson": "^1.1.5",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^13.0.0",
"eslint": "^9.28.0",
"prettier": "^3.5.3",
"ts-loader": "^9.5.2",
"typescript": "^5.8.3",
"typescript-eslint": "^8.34.0",
"webpack": "^5.99.9",
"webpack-cli": "^6.0.1",
"webpack-dev-server": "^5.2.2"

View File

@ -1,4 +1,3 @@
//Array of part names to omit from the BOM
export const rejectedParts = [
'TP1',
'TP2',
@ -20,14 +19,3 @@ export const rejectedParts = [
'IN',
'OUT',
];
//Header titles for the table
export const headers: string[] = ['Part', 'Value'];
//Config object for csv library
export const csvConfig = {
delimiter: `;`,
quote: '"',
ignoreEmpty: true,
includeColumns: /(Part|Value)/,
};

View File

@ -1,9 +1,17 @@
import { csvConfig, rejectedParts } from './config';
// const csv = require('csvtojson');
import csv from 'csvtojson';
import { CSVParseParam } from '../node_modules/csvtojson/v2/Parameters';
import { isJunk } from './utils';
const csv = require('csvtojson');
//CSV Config for EAGLE BOM
const csvConfig = {
delimiter: `;`,
quote: '"',
ignoreEmpty: true,
includeColumns: /(Part|Value)/,
};
export async function getBOM(csvBOM: string) {
const rawBOM = await csv(csvConfig).fromString(csvBOM);
const bom = rawBOM.filter((part: Part) => !rejectedParts.includes(part.Part));
const bom = rawBOM.filter((part: part) => !isJunk(part));
return bom;
}

View File

@ -1,15 +1,16 @@
//Modules
import { getBOM } from './csvToJSON';
import { PartsTable } from './table';
import { headers } from './config';
import { printHTMLtable } from './utils';
import {
createTableBody,
createTableHeader,
htmlTable,
clearTable,
} from './utils';
//DOM elements
const input = document.getElementById('csvInput') as HTMLInputElement;
const csvTextOutput = document.getElementById('csvText') as HTMLPreElement;
const jsonTextOutput = document.getElementById('partsJSON') as HTMLPreElement;
const partsHTML = document.getElementById('partsHTML') as HTMLPreElement;
const htmlTable = document.getElementById('partsTable') as HTMLTableElement;
let csvBOM: string;
@ -36,28 +37,35 @@ function processCSV(fileReader: Event) {
//JSON Object
printJSONbom(csvString);
//Table
clearTable();
createTable();
//TODO Table HTML code
}
//Print JSON
//TODO
async function printJSONbom(csvString: string) {
const bomJSON = await getBOM(csvString);
// console.log(bomJSON);
jsonTextOutput.innerText = JSON.stringify(bomJSON, null, 2);
return bomJSON;
}
//Construct table
async function createTable() {
const bomJSON = await getBOM(csvBOM);
const partsTable = new PartsTable(htmlTable, headers, bomJSON);
const tableMarkup = await partsTable.createTable();
printHTMLtable(tableMarkup as HTMLTableElement, partsHTML);
createTableHeader();
createTableBody(htmlTable, bomJSON);
}
//Add event listener
input.addEventListener('change', handleUpload);
/* TODO
// Format the HTML nicely and output to a pre code block
function displayMarkup() {
const tableCode = document.querySelector('table')!.outerHTML;
const markup = document.getElementById('markup') as HTMLElement;
markup.innerText = beautify(tableCode);
}
// Create a JSON object for Contentful
function makeJSON(csvString: string) {
csv({

20
src/parts.d.ts vendored
View File

@ -1,4 +1,4 @@
enum ComponentType {
enum PartType {
R,
C,
D,
@ -7,26 +7,12 @@ enum ComponentType {
COMPONENT,
}
enum Potentiometers {
VOL = 'VOL',
TONE = 'TONE',
GAIN = 'GAIN',
RES = 'RES',
LEVEL = 'LEVEL',
DIST = 'DIST',
PRESENCE = 'PRESENCE',
}
declare interface Part {
declare interface part {
Part: string;
PartType?: PartType;
Value: string;
}
declare interface Component extends Part {
Designator: string; //A letter like R, C, D etc
ComponentType: ComponentType;
}
declare interface structuredParts {
C: {
[key: string]: string;

View File

@ -2,7 +2,7 @@ export class PartsTable {
constructor(
public htmlTable: HTMLTableElement,
public headers: string[],
public jsonBOM: Part[],
public jsonBOM: part[],
) {}
//Reset table
@ -38,12 +38,4 @@ export class PartsTable {
partValue.appendChild(partValText);
});
}
//Create full table
public createTable(): HTMLElement {
this.clearTable();
this.createTableHeader();
this.createTableBody();
return this.htmlTable;
}
}

View File

@ -1,11 +1,45 @@
import { prettify } from 'htmlfy';
import { rejectedParts } from './config';
//Print HTML code
export async function printHTMLtable(
table: HTMLTableElement,
codeBlock: HTMLPreElement,
) {
codeBlock.innerText = prettify(table.outerHTML);
export function isJunk(element: part): boolean {
// Returns true if element is in the rejected list
return rejectedParts.includes(element.Part);
}
//Table functions
export function clearTable() {
document.querySelector('table')!.innerHTML = '';
}
export const htmlTable = document.getElementById(
'partsTable',
) as HTMLTableElement;
const headers: string[] = ['Part', 'Value'];
export function createTableHeader(): void {
const tHead = htmlTable.createTHead();
const hRow = tHead.insertRow();
//Populate headers with text
for (const header of headers) {
const th = document.createElement('th');
const headerText = document.createTextNode(header);
th.appendChild(headerText);
hRow.appendChild(th);
}
}
export function createTableBody(table: HTMLTableElement, parts: part[]) {
parts.map((component) => {
//Create a row
const tRow = table.insertRow();
//Insert part name
const partName = tRow.insertCell();
const partNameText = document.createTextNode(component.Part);
partName.appendChild(partNameText);
//Insert part value
const partValue = tRow.insertCell();
const partValText = document.createTextNode(component.Value);
partValue.appendChild(partValText);
});
}
// TODO

View File

@ -46,7 +46,7 @@
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
/* Module Resolution Options */
"moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */