@uniweb/scholar 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,332 @@
1
+ /**
2
+ * IEEE Reference Formatter
3
+ *
4
+ * Formats references according to IEEE citation style.
5
+ *
6
+ * @module @uniweb/scholar/bibliography/formatters/ieee
7
+ */
8
+
9
+ import { formatIeeeAuthors, parseAuthorString } from '../utils/authors.js'
10
+ import { formatIeeeDate, getYear } from '../utils/dates.js'
11
+
12
+ /**
13
+ * Format a journal article in IEEE style
14
+ *
15
+ * @param {Object} pub - Publication data
16
+ * @returns {string}
17
+ */
18
+ function formatArticle(pub) {
19
+ const parts = []
20
+
21
+ // Authors
22
+ const authors = Array.isArray(pub.authors)
23
+ ? pub.authors
24
+ : parseAuthorString(pub.authors || pub.author)
25
+ if (authors.length) {
26
+ parts.push(`${formatIeeeAuthors(authors)},`)
27
+ }
28
+
29
+ // Title in quotes
30
+ if (pub.title) {
31
+ parts.push(`"${pub.title},"`)
32
+ }
33
+
34
+ // Journal (italicized)
35
+ if (pub.journal) {
36
+ let journalPart = pub.journal
37
+ if (pub.volume) {
38
+ journalPart += `, vol. ${pub.volume}`
39
+ }
40
+ if (pub.issue) {
41
+ journalPart += `, no. ${pub.issue}`
42
+ }
43
+ if (pub.pages) {
44
+ journalPart += `, pp. ${pub.pages}`
45
+ }
46
+ // Date
47
+ const date = formatIeeeDate(pub.date || pub.year)
48
+ if (date) {
49
+ journalPart += `, ${date}`
50
+ }
51
+ parts.push(`${journalPart}.`)
52
+ }
53
+
54
+ // DOI
55
+ if (pub.doi) {
56
+ parts.push(`doi: ${pub.doi.replace(/^https?:\/\/doi\.org\//, '')}.`)
57
+ }
58
+
59
+ return parts.join(' ')
60
+ }
61
+
62
+ /**
63
+ * Format a book in IEEE style
64
+ *
65
+ * @param {Object} pub - Publication data
66
+ * @returns {string}
67
+ */
68
+ function formatBook(pub) {
69
+ const parts = []
70
+
71
+ // Authors or editors
72
+ const authors = Array.isArray(pub.authors)
73
+ ? pub.authors
74
+ : parseAuthorString(pub.authors || pub.author)
75
+ const editors = Array.isArray(pub.editors)
76
+ ? pub.editors
77
+ : parseAuthorString(pub.editors || pub.editor)
78
+
79
+ if (authors.length) {
80
+ parts.push(`${formatIeeeAuthors(authors)},`)
81
+ } else if (editors.length) {
82
+ parts.push(`${formatIeeeAuthors(editors)}, Ed${editors.length > 1 ? 's' : ''}.,`)
83
+ }
84
+
85
+ // Title (italicized)
86
+ if (pub.title) {
87
+ let title = pub.title
88
+ if (pub.edition && pub.edition !== '1') {
89
+ title += `, ${pub.edition} ed`
90
+ }
91
+ parts.push(`${title}.`)
92
+ }
93
+
94
+ // Place and publisher
95
+ if (pub.address && pub.publisher) {
96
+ parts.push(`${pub.address}: ${pub.publisher},`)
97
+ } else if (pub.publisher) {
98
+ parts.push(`${pub.publisher},`)
99
+ }
100
+
101
+ // Year
102
+ const year = getYear(pub.year || pub.date)
103
+ if (year) {
104
+ parts.push(`${year}.`)
105
+ }
106
+
107
+ return parts.join(' ')
108
+ }
109
+
110
+ /**
111
+ * Format a book chapter in IEEE style
112
+ *
113
+ * @param {Object} pub - Publication data
114
+ * @returns {string}
115
+ */
116
+ function formatInBook(pub) {
117
+ const parts = []
118
+
119
+ // Chapter authors
120
+ const authors = Array.isArray(pub.authors)
121
+ ? pub.authors
122
+ : parseAuthorString(pub.authors || pub.author)
123
+ if (authors.length) {
124
+ parts.push(`${formatIeeeAuthors(authors)},`)
125
+ }
126
+
127
+ // Chapter title in quotes
128
+ if (pub.title) {
129
+ parts.push(`"${pub.title},"`)
130
+ }
131
+
132
+ // Book title
133
+ if (pub.bookTitle) {
134
+ let bookPart = `in ${pub.bookTitle}`
135
+ const editors = Array.isArray(pub.editors)
136
+ ? pub.editors
137
+ : parseAuthorString(pub.editors || pub.editor)
138
+ if (editors.length) {
139
+ bookPart += `, ${formatIeeeAuthors(editors)}, Ed${editors.length > 1 ? 's' : ''}.`
140
+ }
141
+ parts.push(bookPart)
142
+ }
143
+
144
+ // Place and publisher
145
+ if (pub.address && pub.publisher) {
146
+ parts.push(`${pub.address}: ${pub.publisher},`)
147
+ } else if (pub.publisher) {
148
+ parts.push(`${pub.publisher},`)
149
+ }
150
+
151
+ // Year and pages
152
+ const year = getYear(pub.year || pub.date)
153
+ if (year) {
154
+ if (pub.pages) {
155
+ parts.push(`${year}, pp. ${pub.pages}.`)
156
+ } else {
157
+ parts.push(`${year}.`)
158
+ }
159
+ }
160
+
161
+ return parts.join(' ')
162
+ }
163
+
164
+ /**
165
+ * Format conference proceedings in IEEE style
166
+ *
167
+ * @param {Object} pub - Publication data
168
+ * @returns {string}
169
+ */
170
+ function formatConference(pub) {
171
+ const parts = []
172
+
173
+ // Authors
174
+ const authors = Array.isArray(pub.authors)
175
+ ? pub.authors
176
+ : parseAuthorString(pub.authors || pub.author)
177
+ if (authors.length) {
178
+ parts.push(`${formatIeeeAuthors(authors)},`)
179
+ }
180
+
181
+ // Title in quotes
182
+ if (pub.title) {
183
+ parts.push(`"${pub.title},"`)
184
+ }
185
+
186
+ // Conference
187
+ if (pub.booktitle || pub.conference) {
188
+ let confPart = `in ${pub.booktitle || pub.conference}`
189
+ if (pub.address || pub.location) {
190
+ confPart += `, ${pub.address || pub.location}`
191
+ }
192
+ // Year
193
+ const year = getYear(pub.year || pub.date)
194
+ if (year) {
195
+ confPart += `, ${year}`
196
+ }
197
+ if (pub.pages) {
198
+ confPart += `, pp. ${pub.pages}`
199
+ }
200
+ parts.push(`${confPart}.`)
201
+ }
202
+
203
+ // DOI
204
+ if (pub.doi) {
205
+ parts.push(`doi: ${pub.doi.replace(/^https?:\/\/doi\.org\//, '')}.`)
206
+ }
207
+
208
+ return parts.join(' ')
209
+ }
210
+
211
+ /**
212
+ * Format a website/webpage in IEEE style
213
+ *
214
+ * @param {Object} pub - Publication data
215
+ * @returns {string}
216
+ */
217
+ function formatWebsite(pub) {
218
+ const parts = []
219
+
220
+ // Authors or organization
221
+ const authors = Array.isArray(pub.authors)
222
+ ? pub.authors
223
+ : parseAuthorString(pub.authors || pub.author)
224
+ if (authors.length) {
225
+ parts.push(`${formatIeeeAuthors(authors)}.`)
226
+ } else if (pub.organization) {
227
+ parts.push(`${pub.organization}.`)
228
+ }
229
+
230
+ // Title in quotes
231
+ if (pub.title) {
232
+ parts.push(`"${pub.title}."`)
233
+ }
234
+
235
+ // Site name (italicized)
236
+ if (pub.siteName) {
237
+ parts.push(`${pub.siteName}.`)
238
+ }
239
+
240
+ // URL and access date
241
+ if (pub.url) {
242
+ let urlPart = pub.url
243
+ if (pub.accessDate) {
244
+ urlPart += ` (accessed ${formatIeeeDate(pub.accessDate)})`
245
+ }
246
+ parts.push(`${urlPart}.`)
247
+ }
248
+
249
+ return parts.join(' ')
250
+ }
251
+
252
+ /**
253
+ * Format a thesis/dissertation in IEEE style
254
+ *
255
+ * @param {Object} pub - Publication data
256
+ * @returns {string}
257
+ */
258
+ function formatThesis(pub) {
259
+ const parts = []
260
+
261
+ // Author
262
+ const authors = Array.isArray(pub.authors)
263
+ ? pub.authors
264
+ : parseAuthorString(pub.authors || pub.author)
265
+ if (authors.length) {
266
+ parts.push(`${formatIeeeAuthors(authors)},`)
267
+ }
268
+
269
+ // Title in quotes
270
+ if (pub.title) {
271
+ parts.push(`"${pub.title},"`)
272
+ }
273
+
274
+ // Type
275
+ const type = pub.type === 'mastersthesis' ? 'M.S. thesis' : 'Ph.D. dissertation'
276
+ parts.push(type + ',')
277
+
278
+ // Institution
279
+ if (pub.school || pub.institution) {
280
+ parts.push(`${pub.school || pub.institution},`)
281
+ }
282
+
283
+ // Location and year
284
+ if (pub.address) {
285
+ parts.push(`${pub.address},`)
286
+ }
287
+ const year = getYear(pub.year || pub.date)
288
+ if (year) {
289
+ parts.push(`${year}.`)
290
+ }
291
+
292
+ return parts.join(' ')
293
+ }
294
+
295
+ /**
296
+ * Format a reference in IEEE style
297
+ *
298
+ * @param {Object} pub - Publication data
299
+ * @param {Object} [options] - Formatting options
300
+ * @returns {string}
301
+ */
302
+ export function formatIeee(pub, options = {}) {
303
+ const type = (pub.type || pub.entryType || 'article').toLowerCase()
304
+
305
+ switch (type) {
306
+ case 'article':
307
+ case 'journal':
308
+ return formatArticle(pub)
309
+ case 'book':
310
+ return formatBook(pub)
311
+ case 'inbook':
312
+ case 'incollection':
313
+ case 'chapter':
314
+ return formatInBook(pub)
315
+ case 'inproceedings':
316
+ case 'conference':
317
+ case 'proceedings':
318
+ return formatConference(pub)
319
+ case 'webpage':
320
+ case 'website':
321
+ case 'online':
322
+ return formatWebsite(pub)
323
+ case 'thesis':
324
+ case 'phdthesis':
325
+ case 'mastersthesis':
326
+ return formatThesis(pub)
327
+ default:
328
+ return formatArticle(pub)
329
+ }
330
+ }
331
+
332
+ export default formatIeee
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Bibliography Formatters
3
+ *
4
+ * @module @uniweb/scholar/bibliography/formatters
5
+ */
6
+
7
+ export { formatApa } from './apa.js'
8
+ export { formatMla } from './mla.js'
9
+ export { formatChicago } from './chicago.js'
10
+ export { formatIeee } from './ieee.js'
11
+
12
+ import { formatApa } from './apa.js'
13
+ import { formatMla } from './mla.js'
14
+ import { formatChicago } from './chicago.js'
15
+ import { formatIeee } from './ieee.js'
16
+
17
+ /**
18
+ * Available citation style formatters
19
+ */
20
+ export const formatters = {
21
+ apa: formatApa,
22
+ mla: formatMla,
23
+ chicago: formatChicago,
24
+ ieee: formatIeee,
25
+ }
26
+
27
+ /**
28
+ * Format a reference in a specific style
29
+ *
30
+ * @param {Object} publication - Publication data
31
+ * @param {Object} [options]
32
+ * @param {string} [options.style='apa'] - Citation style (apa, mla, chicago, ieee)
33
+ * @returns {string} Formatted reference string
34
+ *
35
+ * @example
36
+ * const ref = formatReference({
37
+ * type: 'article',
38
+ * authors: [{ family: 'Smith', given: 'John' }],
39
+ * year: 2024,
40
+ * title: 'A Study of Something',
41
+ * journal: 'Journal of Studies',
42
+ * volume: '10',
43
+ * pages: '1-10',
44
+ * doi: '10.1234/example'
45
+ * }, { style: 'apa' })
46
+ */
47
+ export function formatReference(publication, options = {}) {
48
+ const { style = 'apa' } = options
49
+
50
+ const formatter = formatters[style.toLowerCase()]
51
+ if (!formatter) {
52
+ throw new Error(`Unknown citation style: ${style}. Available: ${Object.keys(formatters).join(', ')}`)
53
+ }
54
+
55
+ return formatter(publication, options)
56
+ }
@@ -0,0 +1,335 @@
1
+ /**
2
+ * MLA 9th Edition Reference Formatter
3
+ *
4
+ * Formats references according to MLA 9th edition guidelines.
5
+ *
6
+ * @module @uniweb/scholar/bibliography/formatters/mla
7
+ */
8
+
9
+ import { formatMlaAuthors, parseAuthorString } from '../utils/authors.js'
10
+ import { formatMlaDate, getYear } from '../utils/dates.js'
11
+
12
+ /**
13
+ * Format a journal article in MLA style
14
+ *
15
+ * @param {Object} pub - Publication data
16
+ * @returns {string}
17
+ */
18
+ function formatArticle(pub) {
19
+ const parts = []
20
+
21
+ // Authors
22
+ const authors = Array.isArray(pub.authors)
23
+ ? pub.authors
24
+ : parseAuthorString(pub.authors || pub.author)
25
+ if (authors.length) {
26
+ parts.push(`${formatMlaAuthors(authors)}.`)
27
+ }
28
+
29
+ // Title in quotes
30
+ if (pub.title) {
31
+ parts.push(`"${pub.title}."`)
32
+ }
33
+
34
+ // Container (journal name, italicized)
35
+ if (pub.journal) {
36
+ let container = pub.journal
37
+ if (pub.volume) {
38
+ container += `, vol. ${pub.volume}`
39
+ }
40
+ if (pub.issue) {
41
+ container += `, no. ${pub.issue}`
42
+ }
43
+ // Date
44
+ const year = getYear(pub.year || pub.date)
45
+ if (year) {
46
+ container += `, ${year}`
47
+ }
48
+ // Pages
49
+ if (pub.pages) {
50
+ container += `, pp. ${pub.pages}`
51
+ }
52
+ parts.push(`${container}.`)
53
+ }
54
+
55
+ // DOI
56
+ if (pub.doi) {
57
+ const doi = pub.doi.startsWith('http')
58
+ ? pub.doi
59
+ : `https://doi.org/${pub.doi}`
60
+ parts.push(doi)
61
+ }
62
+
63
+ return parts.join(' ')
64
+ }
65
+
66
+ /**
67
+ * Format a book in MLA style
68
+ *
69
+ * @param {Object} pub - Publication data
70
+ * @returns {string}
71
+ */
72
+ function formatBook(pub) {
73
+ const parts = []
74
+
75
+ // Authors or editors
76
+ const authors = Array.isArray(pub.authors)
77
+ ? pub.authors
78
+ : parseAuthorString(pub.authors || pub.author)
79
+ const editors = Array.isArray(pub.editors)
80
+ ? pub.editors
81
+ : parseAuthorString(pub.editors || pub.editor)
82
+
83
+ if (authors.length) {
84
+ parts.push(`${formatMlaAuthors(authors)}.`)
85
+ } else if (editors.length) {
86
+ parts.push(
87
+ `${formatMlaAuthors(editors)}, editor${editors.length > 1 ? 's' : ''}.`
88
+ )
89
+ }
90
+
91
+ // Title (italicized)
92
+ if (pub.title) {
93
+ parts.push(`${pub.title}.`)
94
+ }
95
+
96
+ // Edition
97
+ if (pub.edition && pub.edition !== '1') {
98
+ parts.push(`${pub.edition} ed.,`)
99
+ }
100
+
101
+ // Publisher
102
+ if (pub.publisher) {
103
+ parts.push(`${pub.publisher},`)
104
+ }
105
+
106
+ // Year
107
+ const year = getYear(pub.year || pub.date)
108
+ if (year) {
109
+ parts.push(`${year}.`)
110
+ }
111
+
112
+ return parts.join(' ')
113
+ }
114
+
115
+ /**
116
+ * Format a book chapter in MLA style
117
+ *
118
+ * @param {Object} pub - Publication data
119
+ * @returns {string}
120
+ */
121
+ function formatInBook(pub) {
122
+ const parts = []
123
+
124
+ // Chapter authors
125
+ const authors = Array.isArray(pub.authors)
126
+ ? pub.authors
127
+ : parseAuthorString(pub.authors || pub.author)
128
+ if (authors.length) {
129
+ parts.push(`${formatMlaAuthors(authors)}.`)
130
+ }
131
+
132
+ // Chapter title in quotes
133
+ if (pub.title) {
134
+ parts.push(`"${pub.title}."`)
135
+ }
136
+
137
+ // Book title (italicized)
138
+ if (pub.bookTitle) {
139
+ parts.push(`${pub.bookTitle},`)
140
+ }
141
+
142
+ // Editors
143
+ const editors = Array.isArray(pub.editors)
144
+ ? pub.editors
145
+ : parseAuthorString(pub.editors || pub.editor)
146
+ if (editors.length) {
147
+ parts.push(
148
+ `edited by ${formatMlaAuthors(editors)},`
149
+ )
150
+ }
151
+
152
+ // Publisher
153
+ if (pub.publisher) {
154
+ parts.push(`${pub.publisher},`)
155
+ }
156
+
157
+ // Year
158
+ const year = getYear(pub.year || pub.date)
159
+ if (year) {
160
+ parts.push(`${year},`)
161
+ }
162
+
163
+ // Pages
164
+ if (pub.pages) {
165
+ parts.push(`pp. ${pub.pages}.`)
166
+ }
167
+
168
+ return parts.join(' ')
169
+ }
170
+
171
+ /**
172
+ * Format conference proceedings in MLA style
173
+ *
174
+ * @param {Object} pub - Publication data
175
+ * @returns {string}
176
+ */
177
+ function formatConference(pub) {
178
+ const parts = []
179
+
180
+ // Authors
181
+ const authors = Array.isArray(pub.authors)
182
+ ? pub.authors
183
+ : parseAuthorString(pub.authors || pub.author)
184
+ if (authors.length) {
185
+ parts.push(`${formatMlaAuthors(authors)}.`)
186
+ }
187
+
188
+ // Title in quotes
189
+ if (pub.title) {
190
+ parts.push(`"${pub.title}."`)
191
+ }
192
+
193
+ // Conference/Proceedings (italicized)
194
+ if (pub.booktitle || pub.conference) {
195
+ parts.push(`${pub.booktitle || pub.conference},`)
196
+ }
197
+
198
+ // Year
199
+ const year = getYear(pub.year || pub.date)
200
+ if (year) {
201
+ parts.push(`${year},`)
202
+ }
203
+
204
+ // Pages
205
+ if (pub.pages) {
206
+ parts.push(`pp. ${pub.pages}.`)
207
+ }
208
+
209
+ // DOI
210
+ if (pub.doi) {
211
+ const doi = pub.doi.startsWith('http')
212
+ ? pub.doi
213
+ : `https://doi.org/${pub.doi}`
214
+ parts.push(doi)
215
+ }
216
+
217
+ return parts.join(' ')
218
+ }
219
+
220
+ /**
221
+ * Format a website/webpage in MLA style
222
+ *
223
+ * @param {Object} pub - Publication data
224
+ * @returns {string}
225
+ */
226
+ function formatWebsite(pub) {
227
+ const parts = []
228
+
229
+ // Authors
230
+ const authors = Array.isArray(pub.authors)
231
+ ? pub.authors
232
+ : parseAuthorString(pub.authors || pub.author)
233
+ if (authors.length) {
234
+ parts.push(`${formatMlaAuthors(authors)}.`)
235
+ }
236
+
237
+ // Title in quotes
238
+ if (pub.title) {
239
+ parts.push(`"${pub.title}."`)
240
+ }
241
+
242
+ // Site name (italicized)
243
+ if (pub.siteName) {
244
+ parts.push(`${pub.siteName},`)
245
+ }
246
+
247
+ // Date
248
+ const date = formatMlaDate(pub.date || pub.year)
249
+ if (date) {
250
+ parts.push(`${date},`)
251
+ }
252
+
253
+ // URL
254
+ if (pub.url) {
255
+ parts.push(`${pub.url}.`)
256
+ }
257
+
258
+ return parts.join(' ')
259
+ }
260
+
261
+ /**
262
+ * Format a thesis/dissertation in MLA style
263
+ *
264
+ * @param {Object} pub - Publication data
265
+ * @returns {string}
266
+ */
267
+ function formatThesis(pub) {
268
+ const parts = []
269
+
270
+ // Author
271
+ const authors = Array.isArray(pub.authors)
272
+ ? pub.authors
273
+ : parseAuthorString(pub.authors || pub.author)
274
+ if (authors.length) {
275
+ parts.push(`${formatMlaAuthors(authors)}.`)
276
+ }
277
+
278
+ // Title (italicized)
279
+ if (pub.title) {
280
+ parts.push(`${pub.title}.`)
281
+ }
282
+
283
+ // Year
284
+ const year = getYear(pub.year || pub.date)
285
+ if (year) {
286
+ parts.push(`${year}.`)
287
+ }
288
+
289
+ // Institution and type
290
+ if (pub.school || pub.institution) {
291
+ const type = pub.type === 'mastersthesis' ? 'MA thesis' : 'PhD dissertation'
292
+ parts.push(`${pub.school || pub.institution}, ${type}.`)
293
+ }
294
+
295
+ return parts.join(' ')
296
+ }
297
+
298
+ /**
299
+ * Format a reference in MLA 9th edition style
300
+ *
301
+ * @param {Object} pub - Publication data
302
+ * @param {Object} [options] - Formatting options
303
+ * @returns {string}
304
+ */
305
+ export function formatMla(pub, options = {}) {
306
+ const type = (pub.type || pub.entryType || 'article').toLowerCase()
307
+
308
+ switch (type) {
309
+ case 'article':
310
+ case 'journal':
311
+ return formatArticle(pub)
312
+ case 'book':
313
+ return formatBook(pub)
314
+ case 'inbook':
315
+ case 'incollection':
316
+ case 'chapter':
317
+ return formatInBook(pub)
318
+ case 'inproceedings':
319
+ case 'conference':
320
+ case 'proceedings':
321
+ return formatConference(pub)
322
+ case 'webpage':
323
+ case 'website':
324
+ case 'online':
325
+ return formatWebsite(pub)
326
+ case 'thesis':
327
+ case 'phdthesis':
328
+ case 'mastersthesis':
329
+ return formatThesis(pub)
330
+ default:
331
+ return formatArticle(pub)
332
+ }
333
+ }
334
+
335
+ export default formatMla