142 lines
4.4 KiB
JavaScript
142 lines
4.4 KiB
JavaScript
const express = require('express');
|
||
const cors = require('cors');
|
||
|
||
const app = express();
|
||
const PORT = process.env.PORT || 3000;
|
||
|
||
app.use(cors());
|
||
app.use(express.json());
|
||
app.use(express.text());
|
||
|
||
function removeEmDash(text) {
|
||
if (!text || typeof text !== 'string') {
|
||
return text;
|
||
}
|
||
|
||
function isDateTimeOrPeriod(str) {
|
||
const dateTimePatterns = [
|
||
/^\d+:\d+/,
|
||
/^\d+$/, // number.. could be date, who knows
|
||
/^(January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)$/i, // this is dumb
|
||
/^(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday|Mon|Tue|Wed|Thu|Fri|Sat|Sun)$/i, // days
|
||
/^(Spring|Summer|Fall|Autumn|Winter)$/i, // seasons, did you see that, even autumn my zoul!
|
||
/^\d{4}$/,
|
||
/^Q[1-4]$/i, // quarters for that jerome powell post
|
||
];
|
||
return dateTimePatterns.some(pattern => pattern.test(str.trim()));
|
||
}
|
||
|
||
let result = text
|
||
// 1. parenthetical/interruptive information (word — phrase — word) -> parentheses
|
||
.replace(/([a-zA-Z])\s*—\s*([^—]+?)\s*—\s*([a-zA-Z])/g, '$1 ($2) $3')
|
||
|
||
// 2. date/time ranges (handle early to avoid conflicts)
|
||
.replace(/(\d+:\d+\s*[ap]\.?m\.?)\s*—\s*(\d+:\d+\s*[ap]\.?m\.?)/gi, '$1 to $2')
|
||
|
||
// 3. interrupted speech in dialogue (word—") -> keep as interruption (but clean format)
|
||
.replace(/([a-zA-Z])\s*—\s*$/g, '$1 – ')
|
||
|
||
// 4. attribution quotes ("quote" — Author) -> en dash
|
||
.replace(/"([^"]+)"\s*—\s*([A-Z][^.!?]*)/g, '"$1" – $2')
|
||
|
||
// 5. dramatic pause/reveal (phrase — Word!) -> ellipsis
|
||
.replace(/([a-zA-Z\s]+)\s*—\s*([A-Z][a-zA-Z]*[!.])/g, '$1 ... $2')
|
||
|
||
// 6. sudden break in thought (clause — but/and/or...) -> comma
|
||
.replace(/([a-zA-Z])\s*—\s*(but|and|or|yet|so)\s+/g, '$1, $2 ')
|
||
|
||
// 7. appositive emphasis (noun — a descriptor) -> comma
|
||
.replace(/([a-zA-Z])\s*—\s*(a\s+[a-zA-Z][^.!?]*)/g, '$1, $2')
|
||
|
||
// 8. summary/amplification at end (items — conclusion.) -> semicolon
|
||
.replace(/([a-zA-Z.,\s]*[a-zA-Z.,])\s*—\s*([a-z][^.!?]*\.)/g, '$1 ; $2')
|
||
|
||
// 9. after sentence punctuation -> space
|
||
.replace(/([.!?:;])\s*—\s*([A-Za-z])/g, '$1 $2')
|
||
|
||
// 10. after comma -> space
|
||
.replace(/([,])\s*—\s*([A-Za-z])/g, '$1 $2')
|
||
|
||
// 11. before closing punctuation -> space
|
||
.replace(/([a-zA-Z])\s*—\s*([.,;:!?"'\)\]])/g, '$1 $2')
|
||
|
||
// 12. after opening punctuation -> space
|
||
.replace(/(["'(\[])\s*—\s*([A-Za-z])/g, '$1 $2')
|
||
|
||
// 13. between letters (simple cases) -> comma
|
||
.replace(/([a-zA-Z])\s*—\s*([a-zA-Z])/g, '$1, $2')
|
||
|
||
// 14. leading em dash -> remove
|
||
.replace(/^\s*—\s*/g, '')
|
||
|
||
// 15. trailing em dash -> remove
|
||
.replace(/\s*—\s*$/g, '')
|
||
|
||
// 16. catch-all remaining -> check if it's a date/time range, otherwise en dash
|
||
.replace(/(\S+)\s*—\s*(\S+)/g, (_, left, right) => {
|
||
if (isDateTimeOrPeriod(left) && isDateTimeOrPeriod(right)) {
|
||
return `${left} – ${right}`;
|
||
}
|
||
return `${left} – ${right}`;
|
||
});
|
||
|
||
// sanitation step: clean up spacing issues
|
||
result = result
|
||
// remove spaces before colons and ellipses
|
||
.replace(/\s+\.\.\./g, '...')
|
||
.replace(/\s+:/g, ':')
|
||
.replace(/\s+;/g, ';')
|
||
// clean up multiple spaces
|
||
.replace(/\s+/g, ' ')
|
||
.trim();
|
||
|
||
return result;
|
||
}
|
||
|
||
app.post('/emdash', (req, res) => {
|
||
try {
|
||
const text = typeof req.body === 'string' ? req.body : req.body.text;
|
||
|
||
if (!text) {
|
||
return res.status(400).json({ error: 'Text is required, come on now' });
|
||
}
|
||
|
||
const result = removeEmDash(text);
|
||
res.json({
|
||
original: text,
|
||
result: result
|
||
});
|
||
} catch (error) {
|
||
res.status(500).json({ error: 'Internal server error' });
|
||
}
|
||
});
|
||
|
||
app.get('/emdash', (req, res) => {
|
||
const text = req.query.text;
|
||
|
||
if (!text) {
|
||
return res.status(400).json({ error: 'Text query parameter is required, come on now' });
|
||
}
|
||
|
||
const result = removeEmDash(text);
|
||
res.json({
|
||
original: text,
|
||
result: result
|
||
});
|
||
});
|
||
|
||
app.get('/', (req, res) => {
|
||
res.json({
|
||
message: 'Em Dash API',
|
||
description: 'API to linguistically and grammatically replace em dashes.',
|
||
endpoints: {
|
||
'POST /emdash': 'Send text in body (JSON or plain text)',
|
||
'GET /emdash?text=your-text': 'Send text as query parameter'
|
||
}
|
||
});
|
||
});
|
||
|
||
|
||
app.listen(PORT, () => {
|
||
console.log(`Server running on port ${PORT}`);
|
||
}); |